/*
Theme Name: Smartseer
Theme URI: https://smartseer.com
Author: SMARTSEER GmbH
Description: Block theme for smartseer.com. Layout is assembled from block patterns; design tokens live in theme.json.
Version: 0.1.0
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 8.1
License: GPL-2.0-or-later
Text Domain: smartseer
*/

/* ---------------------------------------------------------------
   Tokens live in theme.json and arrive here as CSS custom properties:
     var(--wp--preset--color--brand)
     var(--wp--preset--font-size--xl)
     var(--wp--preset--spacing--60)
   Never hardcode a colour, size or space in this file. Using the
   variables is what keeps the editor preview and the frontend
   identical, and what stops editors inventing off-brand values.
----------------------------------------------------------------*/

*, *::before, *::after { box-sizing: border-box; }
img, svg, video { max-width: 100%; height: auto; }

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--wp--preset--color--brand);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link:not(:focus) {
  position: absolute !important;
  width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.skip-link:focus {
  position: fixed; inset-block-start: 1rem; inset-inline-start: 1rem;
  z-index: 999; padding: .75rem 1.25rem; border-radius: 8px;
  background: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--base); text-decoration: none;
}

/* --- Header ------------------------------------------------------
   Desktop is a three-column grid: brand left, primary nav dead-centre,
   utility links + CTA right. The 1fr/auto/1fr tracks centre the nav on
   the header itself, not on the space left over by its neighbours, so
   the links stay put however long the brand or the CTA label gets.

   The header switches at 1040px. Centring the nav on the header means both
   side tracks match the wider of the two (the utility links + CTA, ~265px),
   so the row needs 2*265 + nav + gaps + padding ~= 1000px before anything
   wraps. Below that it collapses to logo + hamburger. Every query in this
   file that mentions 1040px is part of that one switch.
------------------------------------------------------------------*/
/* backdrop-filter creates a containing block for fixed-position descendants,
   which traps the mobile navigation overlay inside the header's own box. The
   blur lives on a pseudo-element so the header itself stays a plain box. */
.site-header {
  position: sticky; top: 0; z-index: 100;
}
.site-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: color-mix(in srgb, var(--wp--preset--color--base) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
}
/* The header's inner alignwide is not a .has-global-padding element, so the
   root padding never reaches it and the wordmark hits the viewport edge. */
.site-header > .wp-block-group {
  max-width: var(--wp--custom--layout--wide);
  margin-inline: auto;
  padding-inline: var(--wp--preset--spacing--40);
  display: grid;
  grid-template-columns: 1fr auto;      /* brand | hamburger */
  align-items: center;
  column-gap: var(--wp--preset--spacing--40);
}

/* --- Brand ------------------------------------------------------- */
.brand { display: inline-flex; align-items: center; text-decoration: none; }
.brand__logo { display: block; width: auto; height: 13.5px; }

.site-header .wp-block-navigation a { text-decoration: none; }
.site-header .wp-block-navigation a:hover { color: var(--wp--preset--color--brand); }
.site-header .nav-secondary a { color: var(--wp--preset--color--contrast); }

/* Login / Support live twice: once in the right-hand column for desktop, once
   inside the primary nav so they are present in the mobile overlay. Only ever
   one copy is visible, so screen readers never hear the pair. */
@media (min-width: 1040px) {
  .site-header > .wp-block-group { grid-template-columns: 1fr auto 1fr; }
  .brand__logo { height: 20.5px; }
  .header-actions { justify-self: end; }
  .site-header .nav-overlay-only { display: none; }
}
@media (max-width: 1039.98px) {
  /* .is-layout-flex sets display:flex at (0,1,0); match the block class to win. */
  .wp-block-group.header-actions { display: none; }
  /* Core collapses the navigation into a hamburger below 600px. The header
     cannot fit its desktop row that far down, so move the switch to 1040px by
     re-asserting the two rules core relaxes in its own min-width:600px query. */
  .site-header .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) {
    display: none; position: fixed;
  }
  .site-header .wp-block-navigation__responsive-container-open:not(.always-shown) { display: flex; }
}

/* --- Outlined CTA ------------------------------------------------
   Gradient border: two backgrounds in one box -- a flat fill clipped to the
   padding box, the brand gradient clipped to the border box -- so the 2px
   transparent border shows the gradient through. No wrapper element needed.

   Shared by the header button and the hero one that replaces it below 1040px,
   so the same control never changes appearance just because it moved.
------------------------------------------------------------------*/
.header-cta .wp-block-button__link,
.hero-cta .wp-block-button__link,
.solutions-cta .wp-block-button__link {
  --cta-border: linear-gradient(90deg,
    var(--wp--preset--color--brand-cyan) 0%,
    var(--wp--preset--color--brand-blue) 45%,
    var(--wp--preset--color--brand) 100%);
  border: 2px solid transparent;
  border-radius: 10px;
  white-space: nowrap;
  color: var(--wp--preset--color--contrast);
  background:
    linear-gradient(var(--wp--preset--color--base), var(--wp--preset--color--base)) padding-box,
    var(--cta-border) border-box;
}
.header-cta .wp-block-button__link:hover,
.header-cta .wp-block-button__link:focus-visible,
.hero-cta .wp-block-button__link:hover,
.hero-cta .wp-block-button__link:focus-visible,
.solutions-cta .wp-block-button__link:hover,
.solutions-cta .wp-block-button__link:focus-visible {
  color: var(--wp--preset--color--brand);
  background:
    linear-gradient(var(--wp--preset--color--base), var(--wp--preset--color--base)) padding-box,
    var(--cta-border) border-box;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--wp--preset--color--brand) 12%, transparent);
}

/* Pill button used in the header and throughout */
.is-style-pill .wp-block-button__link,
.wp-block-button.is-style-pill > .wp-block-button__link { border-radius: 999px; }

.is-style-outline > .wp-block-button__link {
  background: transparent; color: var(--wp--preset--color--contrast);
  border: 1.5px solid var(--wp--preset--color--border);
}
.is-style-outline > .wp-block-button__link:hover {
  border-color: var(--wp--preset--color--brand); color: var(--wp--preset--color--brand);
}

/* --- Eyebrow / tag labels --------------------------------------- */
.eyebrow {
  font-size: var(--wp--preset--font-size--xs); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--wp--preset--color--muted-ink);
}
/* The platform tag: the same gradient-border trick as the outlined CTA above,
   but a 6px box rather than a pill and black text rather than brand purple --
   it labels the section, it is not something to click. The design runs its
   border diagonally, deep blue in the top-left corner through brand purple to
   cyan in the bottom-right, which is why this gradient is not --cta-border. */
.tag-outline {
  --tag-border: linear-gradient(135deg,
    color-mix(in srgb, var(--wp--preset--color--brand-blue) 50%, var(--wp--preset--color--ink-deep)) 0%,
    var(--wp--preset--color--brand) 52%,
    var(--wp--preset--color--brand-cyan) 100%);
  display: inline-block; margin: 0;
  padding: var(--wp--preset--spacing--20) 0.625rem;
  border: 2px solid transparent;
  border-radius: 6px;
  background:
    linear-gradient(var(--wp--preset--color--base), var(--wp--preset--color--base)) padding-box,
    var(--tag-border) border-box;
  color: var(--wp--preset--color--contrast);
  font-size: var(--wp--preset--font-size--md);
  line-height: 1.35;
  text-transform: uppercase;
}
/* The same label set in a filled pill -- how the solution cards carry it. */
.tag-pill {
  display: inline-block; margin: 0;
  padding: var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);
  border-radius: 999px;
  background: var(--wp--preset--color--tag-surface);
  color: var(--wp--preset--color--brand-deep);
  font-size: var(--wp--preset--font-size--xs);
  font-weight: 700; letter-spacing: .08em; line-height: 1.2;
  text-transform: uppercase;
}

/* --- Section bands ----------------------------------------------
   Every section on the page is a full-bleed band, and the root block gap
   (spacing-40) opened a 24px strip of the white page background between each
   pair of them. That passed unnoticed while the bands were all white or
   near-white; it is an obvious white line the moment one of them is violet,
   or sits under a gradient that fades into violet. The bands butt together
   instead -- their own top and bottom padding is the separation the design
   asks for.

   Core spaces them with `:where(.is-layout-constrained) > :not(:first-child)`,
   which :where() keeps at one-class specificity, so two classes is enough to
   win without !important.
------------------------------------------------------------------*/
.wp-block-post-content > .alignfull + .alignfull { margin-block-start: 0; }

/* --- Gradient text ---------------------------------------------- */
.text-gradient {
  background: var(--wp--preset--gradient--brand);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* --- Cards ------------------------------------------------------- */
.card {
  background: var(--wp--preset--color--base);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: 18px;
  height: 100%;
}
.card--soft { border: 0; box-shadow: 0 1px 2px rgb(16 18 32 / .04), 0 8px 24px rgb(16 18 32 / .06); }

/* --- Hero --------------------------------------------------------
   The artwork (assets/images/lines-background.svg, 1496x915) is the stage,
   not a column: copy sits top-left over it and the stat card bottom-right.
   Below 782px the artwork is dropped entirely and the two remaining pieces
   stack -- copy, then card.
------------------------------------------------------------------*/
.hero {
  position: relative; overflow: clip;
  /* The stage's height, and the one value everything that has to track the
     stage -- the inner's min-height, the card's lift -- derives from, so they
     cannot drift apart when it is retuned.
     The artwork's own 1496:915 ratio at full-bleed width is the first bound:
     at that height .hero__lines shows the whole canvas uncropped. But the
     ratio alone tied the hero to viewport WIDTH, so every real laptop got a
     hero taller than its screen -- 881px against 819px of room at 1440x900.
     So the height left below the header bounds it too, and the hero ends at
     the fold instead of past it. Under ~1280px wide the ratio is the smaller
     of the two anyway and nothing changes. */
  --hero-stage-h: min(
    calc(100vw * 915 / 1496),
    calc(100svh - var(--hero-headroom))
  );
  /* What sits above the hero and so cannot be used by it: the 57px header plus
     the 24px block gap between it and the hero. (The admin bar adds 32px more,
     but only for logged-in editors.) */
  --hero-headroom: 81px;
}
/* The design stacks the three words. ~7em holds the longest ("Decisions.")
   on its own line at every size without hardcoding <br> into the content. */
.hero__title {
  font-weight: 400;
  font-size: clamp(2.75rem, 5vw, 4rem);
  letter-spacing: -.02em; line-height: 1.08;
  max-width: 7em;
  margin-block-start: 0 !important;
}
.hero__copy { position: relative; z-index: 1; }

/* Hidden below 782px. Phone stacked the artwork between the copy and the card,
   but the card then covered 94% of it, so all that actually showed were slivers
   of bar poking out either side -- artefacts rather than a chart. display:none
   also means phones never download the 253KB SVG at all; the background is
   declared in the >=782px block instead, where it is first painted. */
.hero__lines { display: none; }

.hero__stat-card {
  position: relative; z-index: 1;
  background: var(--wp--preset--color--base);
  border-radius: 16px; border: 1px solid var(--wp--preset--color--border);
  box-shadow: 0 12px 40px rgb(16 18 32 / .12);
  padding: var(--wp--preset--spacing--40);
  max-width: 32rem;
}

/* With the artwork hidden on phone the card simply follows the copy, so this is
   a plain gap where it used to be a negative overlap onto the artwork.
   The selector is deliberately (0,3,0): WordPress emits
   ":root :where(.is-layout-flow) > *" at (0,2,0) zeroing the block margins of
   every child here, so a plain .hero__stat-card rule is silently discarded. */
@media (max-width: 781.98px) {
  .hero .hero__inner > .hero__stat-card {
    margin-block-start: var(--wp--preset--spacing--50);
    margin-inline: auto;
  }
}

@media (min-width: 782px) {
  /* 38% is what the design measures: it is the width that wraps the eyebrow
     onto two lines and the lead onto four, which a wider column does not. */
  .hero__inner {
    display: grid;
    grid-template-columns: minmax(0, 38%) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    /* Subtracting the two paddings makes .hero's *border box*, which is what
       .hero__lines is inset to, land exactly on --hero-stage-h -- the canvas's
       own 1496:915 ratio, so none of the artwork is cropped. */
    min-height: calc(var(--hero-stage-h)
      - var(--wp--preset--spacing--50) - var(--wp--preset--spacing--40));
  }
  .hero__copy { grid-area: 1 / 1; }
  /* Sits clear of the hero's bottom edge, as the design does, rather than flush
     with it. The lift is a relative offset, not a bottom margin: the card is the
     last child of a flow-layout group, and WordPress emits
     ":root :where(.is-layout-flow) > :last-child { margin-block-end: 0 }" at
     (0,2,0), which silently beats any single-class rule here. Offsetting also
     keeps the card floating over the artwork without changing the hero's height.
     vw rather than % because % block offsets resolve against the container's
     *width*, which would drift from the hero's height. */
  .hero__stat-card {
    grid-area: 2 / 2; align-self: end; justify-self: end; margin: 0;
    bottom: min(13vw, 11.5rem);
  }
  /* Absolute against .hero, not .hero__inner, so it spreads past the 1240px
     wide container towards both viewport edges as the design shows.
     contain, because the box is only exactly 1496:915 when --hero-stage-h
     resolves to the ratio, and it misses in BOTH directions:
       - the height bound makes the box wider than the ratio (every laptop);
         sizing by width would then overflow the top, where the tallest bars
         are, and .hero's overflow:clip would cut them off;
       - the ~740px content floor makes the box taller than the ratio under
         ~1100px wide; sizing by height would overflow the sides instead, by
         264px each at 782px.
     contain fits the canvas inside the box whichever way it misses, so the
     artwork is never cropped. What is left over goes to the sides of the
     centred position, or above the bottom-anchored one. */
  .hero__lines {
    display: block;
    background: url(assets/images/lines-background.svg) no-repeat;
    position: absolute; inset: 0; margin: 0; width: auto; height: auto;
    background-size: contain; background-position: center bottom;
  }

  /* Space between the hero and the carousel below it.
     Margin, not padding: .hero__lines is inset to the hero's PADDING box, so it
     paints straight over any bottom padding -- which is why the hero's existing
     24px padding-bottom produces no visible gap at all. Margin sits outside the
     border box, where the artwork cannot reach.
     It also collapses with the next section's 24px block gap and yields the
     larger of the two rather than the sum, so anything <= 24px here does
     nothing. (0,2,0) to clear WordPress's ":root :where(.is-layout-flow) > *"
     margin reset, which beats a bare .hero at (0,1,0). */
  .wp-site-blocks .hero { margin-block-end: var(--wp--preset--spacing--60); }
}

/* A third bound above 1600px: a hard ceiling. The two bounds above would still
   allow a 1359px hero on a 2560x1440 monitor -- it fits the screen, but only
   because the screen is enormous, and every piece of content stays capped at
   the 1240px container regardless. Left uncapped the copy stayed pinned
   top-left and the card a fixed 208px off the bottom, so all the extra height
   landed between them: they drifted 55px apart at 1600 and 642px at 2560, over
   an empty stage.
   Note the stage cannot go below ~740px whatever this says -- .hero__inner is
   a two-row grid, so copy + card + padding is a floor, and it rises further as
   the columns narrow. That only binds on viewports under ~820px tall, which no
   bound here could have fixed anyway.
   1600.02px, not 1600px: same sub-pixel convention as the 781.98px boundary
   above. */
@media (min-width: 1600.02px) {
  .hero {
    --hero-stage-h: min(
      calc(100vw * 915 / 1496),
      calc(100svh - var(--hero-headroom)),
      900px
    );
  }
  /* The lift was min(13vw, 11.5rem), tuned when the stage tracked viewport
     width; past 1600px it no longer does, so derive it from the stage instead
     and the card keeps its place in the composition. .188 is 184/979 -- the
     ratio that clamp resolved to at 1600px, where the two agree. */
  .hero__stat-card { bottom: calc(var(--hero-stage-h) * .188); }
}

/* --- Client logo carousel ---------------------------------------- */
.logo-carousel {
  /* Everything about the motion is a knob, so tuning it never means
     rewriting the keyframes. --logo-carousel-count is written inline by the
     pattern from the actual number of logos -- the loop distance depends on
     it, so it must not be guessed here. */
  /* Slots, not the band, are what respond: seven across a 375px phone leaves
     53px for a 150px logo. Dropping to three keeps them legible, and the loop
     maths is written in terms of this number, so nothing else has to change.
     --step rises as slots fall because a logo crosses the strip in
     visible x step -- holding step at 3.75s would make the phone strip turn
     over in 11s against the desktop's 26s, and read as a much faster scroll. */
  --logo-carousel-visible: 3;   /* logos on screen at once */
  --logo-carousel-count: 7;     /* overridden inline by the pattern */
  --logo-carousel-step: 6s;     /* time one logo takes to move one slot */
  --logo-carousel-fade: 8%;     /* width of the soft edge on each side */

  overflow: hidden;
  /* A mask, not two gradient overlays: it fades the logos against whatever
     colour the band happens to be, so changing the background never leaves a
     pair of stale rectangles behind. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--logo-carousel-fade), #000 calc(100% - var(--logo-carousel-fade)), transparent);
  mask-image: linear-gradient(to right, transparent, #000 var(--logo-carousel-fade), #000 calc(100% - var(--logo-carousel-fade)), transparent);
}

@media (min-width: 560px) {
  .logo-carousel { --logo-carousel-visible: 5; --logo-carousel-step: 4.5s; }
}
@media (min-width: 782px) {
  .logo-carousel { --logo-carousel-visible: 7; --logo-carousel-step: 3.75s; }
}

/* Grid columns rather than flex: a percentage column resolves against the
   strip's own width even though the track overflows it, so "a seventh of what
   you can see" stays true at every viewport without a vw unit (which would
   count the scrollbar and quietly show 6.9 logos). */
.logo-carousel__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc(100% / var(--logo-carousel-visible));
  align-items: center;
  animation: logo-carousel-scroll calc(var(--logo-carousel-step) * var(--logo-carousel-count)) linear infinite;
  will-change: transform;
}

/* The strip runs unbroken. It used to pause on :hover so a visitor could read
   a logo instead of chasing it, but a touch browser fires :hover on tap and
   then keeps it until the next tap elsewhere -- so on a phone the band simply
   stopped and stayed stopped. There is no hover-free way to express "a real
   pointer is resting here", so the pause is gone rather than guarded.
   prefers-reduced-motion still halts it outright, at the end of this file. */

/* Fixed height, not the tallest logo's: the row must not change height when a
   58px-tall logo rotates in behind a 48px one. */
.logo-carousel__item {
  display: flex; align-items: center; justify-content: center;
  height: 3.625rem;
  padding-inline: var(--wp--preset--spacing--30);
}
/* No width/height of its own on purpose. Each img carries its intrinsic size
   as attributes, which is what gives the browser a real box before the file
   arrives -- overriding that with `width: auto` leaves a lazy image at 0x0,
   which never intersects the viewport, so it never loads and stays 0x0. The
   global img reset (max-width:100%, height:auto) scales them down in step on
   narrower desktops. */

@keyframes logo-carousel-scroll {
  from { transform: translateX(0); }
  /* The track's own box is exactly the visible strip, so one full copy of the
     logos is count/visible of it -- land there and the duplicate copy sits
     pixel-for-pixel where the first one started. */
  to   { transform: translateX(calc(-100% * var(--logo-carousel-count) / var(--logo-carousel-visible))); }
}

/* --- Product carousel ("Built for travel") -----------------------
   Four cards and a sliver of the fifth, the way the design lays it out
   across the wide container: 305px cards, 24px gaps and 54px of the fifth
   inside 1370px. Two numbers describe that -- how many cards fit, and how
   much of the next one shows -- so every breakpoint below only restates
   those two and the column width follows.

   Unlike the logo strip this rail never moves on its own. The arrows are
   the control; main.js wires them and sets [data-enhanced], which is what
   reveals them, so a visitor without JavaScript is never shown a dead
   button -- the rail is a native scroll container and still swipes.
------------------------------------------------------------------*/
.travel-carousel {
  --travel-visible: 1;    /* whole cards on screen at once */
  --travel-peek: .35;     /* how much of the next card shows, as a fraction of one */
  --travel-gap: var(--wp--preset--spacing--40);
  position: relative;
}
/* 560px is where two cards are wider than one phone-sized card, and where
   there is finally room for an arrow that does not swallow the artwork --
   below it the rail is swipe-only. 1200px is the design's own layout. */
@media (min-width: 560px)  { .travel-carousel { --travel-visible: 2; --travel-peek: .3; } }
@media (min-width: 782px)  { .travel-carousel { --travel-visible: 3; --travel-peek: .25; } }
@media (min-width: 1200px) { .travel-carousel { --travel-visible: 4; --travel-peek: .177; } }

.travel-rail {
  display: grid;
  grid-auto-flow: column;
  /* Same reasoning as the logo carousel: a percentage column resolves
     against the rail's own box even though the track overflows it, so "a
     quarter of what you can see" stays true at every viewport without a vw
     unit (which would count the scrollbar and quietly show 3.9 cards). */
  grid-auto-columns: calc(
    (100% - var(--travel-gap) * var(--travel-visible))
    / (var(--travel-visible) + var(--travel-peek))
  );
  gap: var(--travel-gap);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* The design draws no scrollbar and the arrows are the control. A native
     bar would also sit under the cards and make the section a different
     height on Windows than on a Mac. Touch still swipes, and the rail
     carries tabindex so it still scrolls from the keyboard. */
  scrollbar-width: none;
}
.travel-rail::-webkit-scrollbar { display: none; }

.travel-card {
  position: relative;
  scroll-snap-align: start;
  aspect-ratio: 305 / 440;      /* the design's card */
  border-radius: 16px;
  overflow: hidden;
  display: flex; align-items: flex-end;
  background: var(--wp--preset--color--surface-strong);
}
.travel-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* The scrim, measured off the design: .7 along the bottom edge, clear at
   51% of the card's height. It sits on the card rather than behind the
   label so the ramp is as tall as the design draws it -- about half the
   card -- instead of only as tall as the text plus its padding. */
.travel-card::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgb(20 24 28 / .7) 0%, rgb(20 24 28 / 0) 51%);
}
.travel-card__label {
  position: relative; z-index: 1; width: 100%;
  padding: var(--wp--preset--spacing--40);
  color: var(--wp--preset--color--base);
  font-size: var(--wp--preset--font-size--lg);
  font-weight: 700; line-height: 1.2;
}

/* Inside the rail's edges rather than outside them: at 1288px and below the
   wide container already reaches the root padding, and an arrow hung off
   the outside would sit on the viewport edge. */
.travel-arrow {
  position: absolute; top: 50%; translate: 0 -50%;
  display: none;
  place-items: center;
  width: 2.75rem; height: 2.75rem; padding: 0;
  border: 1px solid var(--wp--preset--color--border);
  border-radius: 999px;
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
  box-shadow: 0 2px 6px rgb(16 18 32 / .10), 0 8px 24px rgb(16 18 32 / .14);
  cursor: pointer;
}
.travel-arrow--prev { inset-inline-start: var(--wp--preset--spacing--30); }
.travel-arrow--next { inset-inline-end: var(--wp--preset--spacing--30); }
.travel-arrow:hover { color: var(--wp--preset--color--brand); border-color: var(--wp--preset--color--brand); }
/* Kept in the layout rather than removed: the pair is the visitor's bearing
   on how much rail is left, and a control that vanishes at one end moves
   the other one under the cursor. */
.travel-arrow[disabled] { opacity: .35; box-shadow: none; cursor: default; }
.travel-arrow[disabled]:hover { color: var(--wp--preset--color--contrast); border-color: var(--wp--preset--color--border); }
@media (min-width: 560px) {
  .travel-carousel[data-enhanced] .travel-arrow { display: grid; }
}

/* --- Stats -------------------------------------------------------
   Sampled off Home.png: both numbers are clipped from one ramp that spans the
   whole card, not from a ramp fitted to each number. That is what makes
   "4,500+" open on teal and "20M+" on cyan -- the wider number reaches further
   into both ends of the same gradient. Reading the two cards' pixels back as a
   single function of card width, they agree to within a couple of levels
   everywhere they overlap, which is what confirms it is one ramp and not two.

   The colours and stop positions below are those measurements, not the theme
   palette: the design's ramp runs through #5d4bf1 rather than brand purple and
   settles on a greyer #6b47a0, and it is flat either side of the two outer
   stops.
------------------------------------------------------------------*/
.stat__value {
  font-variant-numeric: tabular-nums; line-height: 1; letter-spacing: -.03em;
  /* Fallback where background-clip:text is unsupported; the clip below wins
     everywhere it works. */
  color: var(--wp--preset--color--brand);
  background-image: linear-gradient(90deg, #00d3aa 21%, #5d4bf1 41.5%, #6b47a0 66.7%);
  /* Stops are percentages of the *card*, but the background box is this
     paragraph, which is the card minus its padding. Widening the image by that
     padding on both sides and centring it puts the ramp back on the card's own
     edges, so the percentages above can stay as measured. */
  background-size: calc(100% + 2 * var(--wp--preset--spacing--50)) 100%;
  background-position: center;
  background-repeat: no-repeat;
}
@supports (background-clip: text) or (-webkit-background-clip: text) {
  .stat__value { -webkit-background-clip: text; background-clip: text; color: transparent; }
}

/* --- Dark sections ----------------------------------------------- */
.section-dark { color: var(--wp--preset--color--base); }
.section-dark :where(h1,h2,h3,p,blockquote,cite) { color: inherit; }

.quote { font-size: var(--wp--preset--font-size--xl); line-height: 1.2; text-wrap: pretty; }

/* --- Footer ------------------------------------------------------
   Measured off Footer.png (1512-wide frame, content 72..1440 = 1368px)
   against the theme's 1240px wide container, so every horizontal figure
   and every font-size below is a design pixel scaled by 1240/1368 = .906.
   The design has no rule above the footer -- the only rule sits under the
   wordmark, above the copyright row.
------------------------------------------------------------------*/
.site-footer a { color: inherit; text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }

/* Columns sit at fixed fractions of the container (0 / 17.2 / 68 / 85 %),
   so the block gap has to be 0 -- WP adds it on top of the flex-basis and
   would push every column left of where the design puts it. The stacked
   layout below 782px puts a real gap back. */
.footer-cols.wp-block-columns { column-gap: 0; }

/* Address + contact: 16 design px / 24 design px leading, weight 400.
   14.5px is off the preset scale, but sm (15px) overshoots the design by
   3.4% and the difference shows across a 23-character address line. */
.footer-address { font-size: .90625rem; line-height: 1.45; margin-block: 0; }

/* Both link columns run on a 40 design px baseline rhythm, but the first
   line still sits level with the address block -- so the rhythm is the
   inherited 1.6 leading plus a block gap, not a tall line-height, which
   would push the first line down by half the extra leading. The weight and
   the size differ: the design sets the secondary pair a shade larger. */
.footer-links--primary   { font-size: 1.0625rem; font-weight: 800; }
.footer-links--secondary { font-size: 1.125rem; }

/* --- GIATA badge --------------------------------------------------
   189 x 110 design px of #EDF1F3, no border. Neither the card colour nor
   the sage claim text is a palette colour, so both are literals.
------------------------------------------------------------------*/
/* Two classes, to outrank `.site-footer a { color: inherit }` above: the card
   is a fixed light #EDF1F3, so "Part of" has to stay dark whatever colour the
   footer around it is set in. On every white page this is the colour it was
   inheriting anyway. */
.site-footer .giata-badge { color: var(--wp--preset--color--contrast); }

.giata-badge {
  display: flex; flex-direction: column; justify-content: space-between;
  inline-size: 171px; min-block-size: 100px;
  margin-block-start: 25px;
  padding: 12px 14px 12px 10px;
  border-radius: 3px;
  background: #edf1f3;
}
.site-footer .giata-badge:hover { text-decoration: none; }
.giata-badge__part {
  display: flex; align-items: center; gap: 7px;
  font-size: .90625rem; line-height: 1;
}
.giata-badge__logo { display: block; inline-size: 74px; block-size: auto; }
.giata-badge__claim {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem;
}
.giata-badge__claim-text {
  color: #739499; font-weight: 500;
  font-size: 1.1875rem; line-height: 1.1;
}
.giata-badge__arrow { display: block; flex: none; inline-size: 11px; block-size: auto; margin-block-end: 2px; }
.site-footer .giata-badge:hover .giata-badge__claim-text { text-decoration: underline; }

/* --- Footer wordmark ----------------------------------------------
   The same logo.svg the header uses, drawn to the full width of the wide
   container: 1240 x 145.9px, which is what the design's 1364 x 161 comes
   to at .906. It replaces a live-text "SMARTSEER" that could not match
   the drawn letterforms.
------------------------------------------------------------------*/
.footer-wordmark {
  display: block; inline-size: 100%; block-size: auto;
  margin-block-start: 155px;
}

/* --- Footer legal bar ---------------------------------------------
   The one rule in the design: under the wordmark, above this row.
   #B0C7CF is lighter and bluer than the --border used elsewhere.
------------------------------------------------------------------*/
.footer-legal {
  border-top: 1px solid #b0c7cf;
  margin-block-start: 26px;
  padding-block: 17px 40px;
  align-items: flex-start;
  column-gap: var(--wp--preset--spacing--40);
}
.footer-legal p { margin-block: 0; }
.footer-copyright { font-size: .8125rem; }
.footer-legal__links { display: flex; flex-wrap: wrap; gap: .25rem 20px; font-size: .875rem; }

@media (max-width: 781px) {
  .footer-cols.wp-block-columns { row-gap: var(--wp--preset--spacing--50); }
  .footer-wordmark { margin-block-start: var(--wp--preset--spacing--70); }
}


/* --- Header / hero CTA swap -------------------------------------
   Desktop puts "Book a demo" in the header; mobile puts it in the
   hero, because the header collapses to a hamburger. Matches both
   mockups rather than showing the button twice.
------------------------------------------------------------------*/
/* .wp-block-buttons.is-layout-flex sets display:flex at (0,2,0), so a bare
   .header-cta selector loses. Match the block class to win the cascade. */
.wp-block-buttons.header-cta { display: none; }
@media (min-width: 1040px) {
  .wp-block-buttons.header-cta { display: flex; }
  .wp-block-buttons.hero-cta   { display: none; }
}

/* --- Hero stat card ---------------------------------------------- */
.hero__stat-card > * { margin-block: 0; }
.hero__stat-eyebrow { color: var(--wp--preset--color--brand); }
.hero__stat-value {
  font-size: var(--wp--preset--font-size--mega); font-weight: 800;
  /* Manrope's default figures are proportional -- "1" is a third narrower than
     "0" -- which makes "7-15" read unevenly. Match .stat__value and use tnum. */
  font-variant-numeric: tabular-nums;
  line-height: 1; letter-spacing: -.03em; margin-block-start: .35rem !important;
  /* Teal-to-blue rather than the --brand gradient, which runs the other way and
     would put purple on the "7". color:transparent cascades to the % span, so
     it is clipped from the same background and the ramp continues across it. */
  background: linear-gradient(95deg,
    var(--wp--preset--color--brand-green) 0%,
    var(--wp--preset--color--brand-cyan) 35%,
    var(--wp--preset--color--brand-blue) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  width: fit-content;
}
.hero__stat-value span { font-size: .5em; }
.hero__stat-label {
  font-size: var(--wp--preset--font-size--md); font-weight: 600;
  color: var(--wp--preset--color--contrast);
}
.hero__stat-rate {
  display: flex; flex-wrap: wrap; align-items: center; column-gap: .75rem; row-gap: .1rem;
  margin-block-start: var(--wp--preset--spacing--30) !important;
  padding: var(--wp--preset--spacing--30);
  border-radius: 12px;
  background: var(--wp--preset--color--surface);
  font-size: var(--wp--preset--font-size--sm);
  color: var(--wp--preset--color--muted-ink);
}
.hero__stat-rate strong {
  font-size: var(--wp--preset--font-size--xl); font-weight: 700;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
  color: var(--wp--preset--color--contrast);
}
.hero__stat-rate span { flex: 1; min-width: 9rem; line-height: 1.35; }
.hero__stat-note {
  margin-block-start: var(--wp--preset--spacing--30) !important;
  font-size: var(--wp--preset--font-size--xs);
  color: var(--wp--preset--color--muted-ink);
}

/* --- Solution grid ------------------------------------------------
   The heading is set light here rather than in theme.json: the design draws
   every section heading at regular weight, but the rest of the page still
   runs on the theme's 700 until each section has been through review.
------------------------------------------------------------------*/
.solutions__heading,
.travel__heading,
.cta__heading { font-weight: 400; }

.solution-grid, .stat-grid, .travel-rail { list-style: none; margin: 0; padding: 0; }
.solution-grid {
  display: grid; gap: var(--wp--preset--spacing--50);
  grid-template-columns: 1fr;
}
/* Every breakpoint in this section is set by one measurement: the longest tag,
   "Search & ranking experiences", is ~256px wide as a single-line pill. A
   column narrower than that plus the card's inset wraps it onto two lines and
   the card stops looking like the design, so each split waits until its
   columns clear it -- two from 720px, three from 1200px. */
@media (min-width: 720px)  { .solution-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .solution-grid { grid-template-columns: repeat(3, 1fr); } }

/* A centred column. The inset is deeper below the tag than above the icon,
   which is what stops the slack left by a one-line title reading as a gap
   the other cards in the row do not have. */
.solution-card {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding:
    var(--wp--preset--spacing--40)
    var(--wp--preset--spacing--40)
    var(--wp--preset--spacing--50);
}
/* Same measurement, one step later: the design's 40px inset needs a ~336px
   card to clear the tag, which two columns only reach from 800px. */
@media (min-width: 800px) {
  .solution-card {
    padding:
      var(--wp--preset--spacing--50)
      var(--wp--preset--spacing--50)
      var(--wp--preset--spacing--60);
  }
}
/* A fixed slot, because the six icons are 59-87px tall at their natural size.
   Centring them in one box is what lines the titles up across a row. */
.solution-card__icon {
  display: flex; align-items: center; justify-content: center;
  block-size: var(--wp--preset--spacing--70);
  margin-block-end: var(--wp--preset--spacing--50);
}
.solution-card__icon img { max-block-size: 100%; inline-size: auto; }
.solution-card__title {
  margin: 0;
  font-size: var(--wp--preset--font-size--lg);
  line-height: 1.25;
}
.solution-card .tag-pill { margin-block-start: var(--wp--preset--spacing--30); }

/* --- Engine ------------------------------------------------------
   assets/images/engine-circle.svg is one 1587x1008 canvas holding the ring,
   the wires that run off both edges of the viewport and the dotted tail. Three
   numbers measured off it drive every offset below, so re-exporting the
   artwork means re-measuring these and nothing else:

     ring centre   42.94% / 47.17% of the canvas
     ring width    28.17% of the canvas width, outer edge to outer edge
     drawn area    16.67%-86.11% of the canvas height (the export carries
                   168px of empty canvas above the top wire and 140px below
                   the bottom one), so its centre sits at 51.39%

   Placing the canvas is then always the same move: an inset that says where
   the anchor goes, and a translate that says which point of the canvas is the
   anchor. The wide layout anchors the ring, because the ring has to line up
   with the copy beside it; the stacked one anchors the drawn area, because
   there the artwork is alone on its row and what matters is that the band
   shows all of it and nothing else.
------------------------------------------------------------------*/
.engine { position: relative; overflow: clip; }
.engine__stage { position: relative; }
/* The container for the label's cqi sizing as well as the positioning parent
   for it -- one box doing both is what keeps the label locked to the ring at
   any zoom level. */
.engine__art { position: absolute; container-type: inline-size; }
.engine__art img { display: block; width: 100%; }

/* 1.79cqi is the design's ratio of label size to ring size (27.5px inside a
   371px ring) rewritten against the canvas, so the words keep their place in
   the ring at every breakpoint instead of needing a size per zoom level. */
.engine__label {
  /* left/top put the box on the ring's geometric centre; the -55% then slides
     it 5% of its own width back to the left, where it looks centred -- the
     ring's heavy dark arc is on the right and pulls the eye that way. */
  position: absolute; left: 42.94%; top: 47.17%; translate: -55% -50%;
  width: 18%; margin: 0;
  font-size: clamp(0.8125rem, 1.79cqi, 1.75rem);
  line-height: 1.24; text-align: center; text-transform: uppercase;
  color: var(--wp--preset--color--contrast);
}

/* Below 1200px the copy is taller than the gap between the two wires, so the
   two stop sharing a row: the artwork takes a full-bleed band of its own and
   the copy sits underneath it. The canvas never draws smaller than 860px --
   the width at which the label is still a readable 15px -- so a phone gets a
   zoomed crop of the ring while a tablet gets the whole picture. */
@media (max-width: 1199.98px) {
  /* .alignfull on the div is what widens it past the group's inline padding:
     core's constrained layout centres every other child with an !important
     margin, and alignfull is the documented way out of it. */
  .engine .engine__stage {
    --engine-art: max(121vw, 860px);
    /* The drawn artwork is 69.4% of the canvas tall, so this is its height and
       the band shows all of it and none of the empty canvas around it. */
    height: calc(var(--engine-art) * .4408);
    overflow: clip;
  }
  .engine .engine__art {
    width: var(--engine-art); left: 50%; top: 50%;
    translate: -42.94% -51.39%;
  }
  /* Core only stops the columns below 782px; here they have to stop early. */
  .engine .wp-block-columns { flex-wrap: wrap; }
  /* Full width is 110 characters a line at tablet sizes; --measure holds the
     copy to roughly the width it has in the wide layout. */
  .engine .engine__copy {
    flex-basis: 100% !important;
    max-width: var(--wp--custom--layout--measure);
  }
  /* Only there to reserve the ring's half of the wide row. */
  .engine__spacer { display: none; }
}

@media (min-width: 1200px) {
  /* One grid cell holds both: the artwork is the backdrop the copy sits on,
     not a column beside it. */
  .engine { display: grid; align-items: center; }
  .engine > .engine__stage,
  .engine > .wp-block-columns { grid-area: 1 / 1; }
  .engine > .wp-block-columns { margin-block-start: 0; }
  /* .alignfull in the selector only to outweigh the negative margins core
     gives that class -- here the stage is positioned, not bled. Sized to the
     wide container rather than the viewport so the composition stops growing
     where the copy does; the canvas still spills past it on both sides, and
     .engine's clip trims that at the viewport edges. */
  .engine > .engine__stage.alignfull {
    /* Width is explicit because a grid item with auto margins does not
       stretch: left at width:auto this box is empty and would measure zero. */
    width: 100%;
    max-width: var(--wp--style--global--wide-size);
    margin-inline: auto;
    /* .56 is the drawn artwork's height at this width: the section is as tall
       as the picture and no taller, as the design has it. */
    aspect-ratio: 1 / .56;
  }
  /* 121% is what makes the ring 34% of the container across, and 34.7% is
     where the design puts its centre -- clear of the copy column at 60%. */
  .engine .engine__art {
    width: 121%; left: 34.7%; top: 50%;
    translate: -42.94% -47.17%;
  }
}

/* --- Stats --------------------------------------------------------
   The pair is narrower than the wide container: measured off Home.png the two
   cards and their gutter span 1136 of the design's 1372px container, which is
   1028px against the theme's 1240 and leaves each card at 502px.

   `.alignwide` is in the selector only to outweigh the max-width core's
   constrained layout gives that class -- without it the rule loses and the
   cards stretch to the full 1240.
------------------------------------------------------------------*/
.stat-grid.alignwide {
  display: grid; gap: var(--wp--preset--spacing--40);
  grid-template-columns: 1fr;
  max-width: 1028px; margin-inline: auto;
}
@media (min-width: 720px) { .stat-grid.alignwide { grid-template-columns: repeat(2, 1fr); } }
.stat { padding: var(--wp--preset--spacing--50); text-align: center; }
.stat > * { margin-block: 0; }
/* Not the `mega` preset: the design sets these numbers at 108 design px / 97.6
   CSS px, half again the 80px `mega` tops out at, and `mega` is also the hero's
   stat size. Weight is 500, not the 800 this carried -- the figures' stems
   measure 10.7px against an 81px figure height, and Manrope only draws that
   ratio around 500. */
.stat__value { font-size: clamp(3.25rem, 7.9vw, 6.1rem); font-weight: 500; }
.stat__label {
  margin-block-start: var(--wp--preset--spacing--20) !important;
  font-size: var(--wp--preset--font-size--sm); color: var(--wp--preset--color--muted-ink);
}

/* --- Testimonial / CTA -------------------------------------------- */
/* The card is a photograph, not the flat navy panel it used to be: the easyJet
   wing shot under the design's #2A1944 veil at 60%, which is what holds the
   white text legible over the bright sky at the top of the frame. The veil is
   a gradient layer in the same `background-image` as the photo rather than a
   pseudo-element, so the two crop, round and scale as one thing.

   The colour underneath shows only if the webp never arrives, and it has to
   stay dark: .section-dark has already turned every word in here white.

   The 2x file is 43KB, so `image-set` here is a courtesy rather than a saving;
   the plain `url()` above it is what Safari before 17 gets. */
.testimonial {
  background-color: var(--wp--preset--color--ink-deep);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  background-image:
    linear-gradient(rgb(42 25 68 / .6), rgb(42 25 68 / .6)),
    url(assets/images/testimonial-easyjet-1240.webp);
  background-image:
    linear-gradient(rgb(42 25 68 / .6), rgb(42 25 68 / .6)),
    image-set(
      url(assets/images/testimonial-easyjet-1240.webp) 1x,
      url(assets/images/testimonial-easyjet-2480.webp) 2x);
}
/* The blockquote's inline margins are the UA's own `margin: 1em 40px`, and on
   a full-width quote they are 80px the design does not have -- enough to pull
   the third line back and change where all three break. */
.testimonial .wp-block-quote { border: 0; padding: 0; margin-inline: 0; margin-block: var(--wp--preset--spacing--20) 0; }
.testimonial__logo { display: block; height: auto; max-width: 100%; }

/* The design opens the quote with a standalone lilac " instead of the inline
   one the paragraph used to carry, set bold and about half again the body
   size. Bold matters: the mark's ink measures 11x10 in the comp, and only
   Manrope's 700 weight has that near-square ratio -- at 400 the glyph is
   taller than it is wide. The colour is measured off the comp and is not a
   palette entry; nothing else on the page uses it.

   It is decorative, so it is hidden from assistive tech and the paragraph
   keeps its closing " as the only quote mark a screen reader meets. The tight
   line box is what holds the mark close to the text beneath it -- at
   line-height 1 the glyph sits in the top third of a 36px box and the rest
   reads as an accidental gap. */
.quote-mark {
  /* The comp leaves 95px between the logo and the mark and only 33px between
     the mark and the first line -- the mark belongs to the quote, not to the
     logo. Both are measured ink-to-ink and scaled by 1240/1372, the ratio
     between this card and the comp's. The gap below is set on the blockquote
     because the flow layout puts it there, not on the mark. */
  margin-block: var(--wp--preset--spacing--70) 0;
  color: #b87dff;
  font-size: 2.25rem;
  font-weight: 700;
  line-height: .55;
}
.testimonial cite {
  display: block; margin-block-start: var(--wp--preset--spacing--40);
  font-size: var(--wp--preset--font-size--sm); font-style: normal; opacity: .8;
}

/* --- Closing CTA --------------------------------------------------
   The band is the client's artwork (assets/images/cta-background-*.webp),
   exported from CTA-background.svg -- which was a 3841x2161 JPEG in an SVG
   wrapper, cropped by that file's own transform to the 1512x654 the design
   draws. The crop is baked into the webp so the browser downloads 19KB
   instead of 1MB of base64.

   At 1512px wide `cover` therefore draws the artwork at exactly its natural
   size, nothing cropped, and holding the section to that same 1512:654 ratio
   keeps it that way at every narrower width. The 654px ceiling stops a wide
   monitor stretching the band into a wall -- past 1512px the image crops top
   and bottom instead, which diagonals survive and a composition with a
   subject would not.

   Content is centred in whatever height that leaves. The group's own padding
   is the floor underneath it, for the stacked cards on a phone.
-------------------------------------------------------------------*/
.section-cta {
  display: grid;
  align-content: center;
  min-block-size: min(calc(100vw * 654 / 1512), 654px);
  background-color: var(--wp--preset--color--ink-deep);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  background-image: url(assets/images/cta-background-1512.webp);
  background-image: image-set(
    url(assets/images/cta-background-1512.webp) 1x,
    url(assets/images/cta-background-3024.webp) 2x);
}

/* The design breaks the headline after "into" and lets the second line run
   long, which is not the break `text-wrap: balance` would pick. A measure
   wide enough for "profitable business decisions" but too narrow to pull
   "profitable" up is what reproduces it. Two classes because WordPress's own
   constrained-layout rule sets max-width at two classes as well. */
.section-cta .cta__heading { max-inline-size: 13.75em; }

/* A fixed pair, not a fluid grid: the design's cards are 325px wide with a
   24px gutter inside its 1372px frame, which is 294 and 22 once scaled to the
   theme's 1240px container. They stack below 620px, where the pair plus the
   root padding stops fitting. */
.section-cta .cta-grid {
  list-style: none; margin-inline: auto; padding: 0;
  /* The design leaves 49px under the headline, between spacing-50 and -60 and
     tighter than the spacer every other section on the page uses -- hence no
     spacer block here and a margin that replaces the root block gap. */
  margin-block-start: calc(var(--wp--preset--spacing--40) * 2);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--wp--preset--spacing--40);
  max-inline-size: 610px;
}
@media (min-width: 620px) {
  .section-cta .cta-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Same lilac on both cards -- fill on the demo, outline on the consult. The
   filled one keeps the border so the two measure the same to the pixel; its
   own fill hides it. */
.cta-card {
  display: flex; flex-direction: column;
  /* 16px, not the 24px a card this size would normally take: the design's
     inset is 18px once scaled, and the extra 8px on each side wraps the
     consult card's copy onto a fourth line. */
  padding: var(--wp--preset--spacing--30);
  border: 1px solid var(--wp--preset--color--brand-lilac);
  border-radius: 16px;
}
.cta-card--solid { background-color: var(--wp--preset--color--brand-lilac); }
.cta-card > * { margin-block: 0; }
.cta-card__title { font-size: var(--wp--preset--font-size--lg); line-height: 1.25; }
.cta-card__body { margin-block-start: var(--wp--preset--spacing--30); line-height: 1.4; }
/* `auto` rather than a fixed space above the link: the two bodies are three
   lines each today, and this is what keeps the links on one line if an editor
   makes one of them four. */
.cta-card__action {
  margin-block-start: auto;
  padding-block-start: var(--wp--preset--spacing--30);
}

/* Manrope's subsets stop either side of U+2192 (the latin range jumps 2191 to
   2193), so a literal arrow in the copy falls back to the system font and
   comes out a different weight and size from the design. It is drawn here
   instead -- it is decoration, it never needs translating, and an editor
   cannot delete half of it. currentColor through the mask keeps it the same
   white as the label. */
.cta-card .cta-link {
  --cta-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 12'%3E%3Cpath d='M0 6h12.6M7.6 1l5 5-5 5' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E");
  display: inline-flex; align-items: center; gap: .65em;
  color: inherit; text-decoration: none;
  font-size: var(--wp--preset--font-size--sm); font-weight: 700;
}
.cta-card .cta-link:hover { text-decoration: underline; }
.cta-card .cta-link::before {
  content: ""; flex: none;
  inline-size: .85em; aspect-ratio: 14 / 12;
  background-color: currentColor;
  -webkit-mask: var(--cta-arrow) no-repeat center / contain;
  mask: var(--cta-arrow) no-repeat center / contain;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  /* Collapsing the marquee's duration would jump it straight to the end
     frame and park it mid-logo, so stop it outright and hand the visitor a
     scrollbar instead of a broken-looking strip. */
  .logo-carousel__track { animation: none !important; }
  .logo-carousel { overflow-x: auto; }
}

/* --- Contact modal ------------------------------------------------
   Measured off "Contact HQ pop-up.png": 674px wide, 48px padding, 62px
   fields on a 16px gap, 1px #b0c7cf borders.

   Two colours here are the design's rather than the theme's, because the
   theme has no token for them: the field border, and the grey-teal the
   placeholders are set in. Note also that the design's dark is #0e1c1e --
   Home.png uses it for the hero heading too -- while the theme's `contrast`
   preset is #0e1116. The button below follows the preset so it matches the
   rest of the site as built; the discrepancy is site-wide, not this
   component's to settle.                                             */

.contact-modal {
  --contact-field-border: #739499;
  --contact-placeholder: #739499;
  --contact-error: #b32d2e;

  inline-size: 674px;
  max-inline-size: calc(100vw - 2rem);
  max-block-size: calc(100vh - 2rem);
  padding: 60px 48px 58px;
  border: 0;
  border-radius: 16px;
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* 70% over the design's #0e1c1e -- solved from the export, where the white
   page behind the scrim reads back as rgb(87 97 98). */
.contact-modal::backdrop { background: rgb(14 28 30 / .7); }

/* showModal() makes the page inert but does not stop it scrolling behind the
   dialog, which on a phone reads as the modal drifting. */
html.has-modal-open { overflow: hidden; }

.contact-modal__title {
  margin: 0 0 48px;
  font-size: 32px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -.01em;
}

.contact-modal__close {
  position: absolute;
  inset-block-start: 38px;
  inset-inline-end: 38px;
  display: grid;
  place-items: center;
  inline-size: 21px;
  aspect-ratio: 1;
  padding: 0;
  border: 0;
  background: none;
  color: var(--wp--preset--color--contrast);
  cursor: pointer;
}
.contact-modal__close svg {
  inline-size: 100%;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  fill: none;
}
.contact-modal__close:hover { opacity: .6; }

/* --- Contact form fields ------------------------------------------ */

/* 282 + 16 + 282 = the 580px content width. The half-width pair are the only
   fields that share a row, so they take the natural track and everything else
   spans both -- no wrapper element per row. */
.contact-form__fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.contact-form__field { grid-column: 1 / -1; min-inline-size: 0; }
.contact-form__field--half { grid-column: auto; }

/* The design labels fields with placeholders alone. The label is kept in the
   markup and hidden here: it is what a screen reader announces and what
   autofill matches on, neither of which a placeholder does reliably. */
.contact-form__label {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.contact-form__input {
  /* Form controls are inline-level by default, which gives the wrapper a line
     box and a few pixels of leading under the control -- enough to push the
     last grid row, the button and the legal line off the design's rhythm. */
  display: block;
  inline-size: 100%;
  block-size: 56px;
  padding: 0 14px;
  border: 1px solid var(--contact-field-border);
  border-radius: 8px;
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
  font-family: inherit;
  font-size: 16px;
  line-height: 1.4;
}
.contact-form__input::placeholder { color: var(--contact-placeholder); opacity: 1; }
.contact-form__input:focus-visible {
  outline: 2px solid var(--wp--preset--color--brand);
  outline-offset: -1px;
}

.contact-form__input--textarea {
  block-size: 120px;
  padding: 18px 14px;
  resize: vertical;
}

/* An empty required select is :invalid, which is how the "Country" option
   holds the placeholder colour until a real country is chosen. */
.contact-form__input--select {
  appearance: none;
  padding-inline-end: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' stroke='%230e1c1e' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 18px center;
}
.contact-form__input--select:invalid { color: var(--contact-placeholder); }

/* --- Validation feedback ------------------------------------------ */

.contact-form__error {
  display: none;
  margin-block-start: 6px;
  color: var(--contact-error);
  font-size: var(--wp--preset--font-size--sm);
  line-height: 1.35;
}
.contact-form__field.is-invalid .contact-form__error { display: block; }
.contact-form__field.is-invalid .contact-form__input { border-color: var(--contact-error); }

.contact-form__status {
  margin: 16px 0 0;
  color: var(--contact-error);
  font-size: var(--wp--preset--font-size--sm);
}
.contact-form__status:empty { display: none; }

/* Off-screen rather than display:none: a bot that reads computed styles skips
   a hidden input, but still fills this one. */
.contact-form__hp {
  position: absolute;
  inset-inline-start: -9999px;
  inline-size: 1px; block-size: 1px;
  overflow: hidden;
}

/* --- Submit and legal line ---------------------------------------- */

.contact-form__submit {
  /* block + fit-content rather than the default inline-block: as an inline the
     button sits on a line box and inherits its leading, which pushed it ~8px
     below where the design puts it and moved the legal line with it. */
  display: block;
  inline-size: fit-content;
  margin-block-start: 48px;
  padding: 0 26px;
  block-size: 51px;
  border: 0;
  border-radius: 5px;
  background: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--base);
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}
.contact-form__submit:hover { opacity: .88; }
.contact-form__submit[disabled] { opacity: .55; cursor: default; }

.contact-form__legal {
  margin: 24px 0 0;
  font-size: 14px;
  font-weight: 300;
  line-height: 1.4;
}
.contact-form__legal a { color: inherit; text-decoration: underline; }

.contact-form__success h3 {
  margin: 0 0 12px;
  font-size: 24px;
  font-weight: 500;
}
.contact-form__success p { margin: 0; color: var(--wp--preset--color--muted-ink); }

/* One column below the width where two 282px fields plus their gap and the
   48px padding stop fitting. */
@media (max-width: 720px) {
  .contact-modal { padding: 40px 24px 36px; border-radius: 12px; }
  .contact-modal__title { margin-block-end: 32px; font-size: 26px; }
  .contact-modal__close { inset-block-start: 26px; inset-inline-end: 24px; }
  .contact-form__fields { grid-template-columns: 1fr; }
  .contact-form__submit { inline-size: 100%; }
}


/* --- Legal pages (Impressum, Privacy Policy, Cookies) --------------
   Measured off Impressum.png (1512-wide frame, container 72..1440 =
   1368px) against the theme's 1240px container, so every horizontal
   figure and every font-size below is a design pixel scaled by
   1240/1368 = .906 -- the same factor the footer uses.

   The page is one left-aligned column: the copy starts at the wide
   container's left edge and wraps at 742px, rather than sitting in the
   centred 820px `contentSize` a page normally gets. Nothing is centred,
   so the blocks stay in normal flow and only the gaps below are set.
------------------------------------------------------------------*/
/* The root block gap drops 24px above <main> and another 24px above the
   footer. This page sets its own space at both ends, so take those two
   out and keep the figures in one place rather than netted off. */
.legal, .legal + .wp-block-template-part { margin-block-start: 0; }

.legal__inner {
  /* 87 design px of air under the header before the cap of the title --
     see .legal__title for the 15.8px its own line box puts above that cap. */
  padding-block-start: 63px;
  /* The footer brings its own 80px of padding-top; this is the rest of
     the 148 design px the design leaves under the last line. */
  padding-block-end: 29px;
}

/* 92 design px / 83 CSS, weight 400. The one display-size heading on the
   site that is not the hero's 800 -- measured by ink width over cap
   height, which is letter-spacing- and antialiasing-independent and puts
   it on 400 to three digits. 5.5vw hits 83px at the 1512 frame width. */
.legal__title {
  margin-block: 0;
  font-size: clamp(2.5rem, 5.5vw, 5.1875rem);
  font-weight: 400;
  line-height: 1.05;
}

/* 20 design px / 18 CSS on a 30 design px / 1.5 leading. Between the
   theme's `md` and `lg` presets, so it is a literal -- `lg` renders 20px
   at this width and overshoots the design by 11%.

   The column is 818 design px / 742 CSS: both legal exports run their copy
   from x=70 to a hard right edge at x=890 and never past it. This was 680
   until the Privacy Policy went in, which is 60px short and re-wrapped
   every paragraph on both pages a word or two early -- on the Impressum,
   "...under the general laws" lost "laws" to the next line. Measured by
   rendering the design's own line breaks at 18px Manrope and finding the
   window that reproduces them: the longest line on either page needs
   740.1, and the narrowest line that must NOT take its next word allows
   748.2, so anything in [741, 748] is right and the midpoint is quietest.

   It does not land every line: Figma shapes the Impressum's "...we are not
   obligated" about 6px wider than the browser does, so that one line takes
   its next word here. No column width fixes that -- the same export wants
   >733.9 two lines later, which contradicts it. */
.legal__body {
  max-width: 742px;
  font-size: 1.125rem;
  line-height: 1.5;
  /* The policy prints four bare URLs, and the longest -- the BfDI's
     Laender-node.html -- is 511px of unbreakable word. On a 375px phone
     it ran straight out of the viewport and gave the whole document a
     horizontal scrollbar. Only bites when a word cannot fit at all, so
     the Impressum is unaffected. */
  overflow-wrap: break-word;
  /* Manrope ships "(c)" -> © as a standard ligature, so the GDPR citation
     "Art 6(1) (c) GDPR" rendered as "Art 6(1) © GDPR" -- the markup was
     right and only the glyph was wrong, which is the hard kind to spot.
     Turning off `liga` here is the narrowest fix: (c) is the only
     sequence in the font that does this -- (a), (b), (d), (e), (f), (r),
     (tm), (1) and (2) are all left alone -- and the only ligatures it
     costs are Manrope's fi/fl, worth about 0.4px on a line of legal
     copy. */
  font-variant-ligatures: no-common-ligatures;
}
/* 182 design px from the title's cap to the first line's, which is a
   good deal more than the block gap core would give it. */
.legal__inner > .legal__body { margin-block-start: 86px; }

/* The design draws the contact details as plain body text -- no link
   colour and no rule under them. Same treatment the footer gives its
   address block, so the underline only appears on hover and focus. */
.legal__body a { color: inherit; text-decoration: none; }
.legal__body a:hover,
.legal__body a:focus-visible { text-decoration: underline; }

/* Core's flow layout puts the block gap on every child; the design runs
   on its own rhythm, so take that over. A blank line between paragraphs
   is exactly one line of the block they sit in -- 1.5em -- which is why
   this is em and not a preset. */
.legal__body > * { margin-block: 0; }
.legal__body > * + * { margin-block-start: 1.5em; }

/* A subhead sits one plain line above its paragraph: same size, same
   leading, no gap -- only the weight changes.

   700 is the theme's heading weight, not a measured figure: at this size
   the design export cannot be read for weight at all (cap 15px, where a
   0.3px antialiasing difference on the band is worth ~100 weight units --
   the same method returns 349 for body copy that is certainly 400). The
   design is clearly heavier here than the body and that is as far as the
   PNG goes. If Figma says 500 or 600, this line and the h2 below are the
   only two to change. */
.legal__body h3 {
  font-size: 1em;
  font-weight: 700;
  line-height: inherit;
}
.legal__body > h3 + * { margin-block-start: 0; }
.legal__body > * + h3 { margin-block-start: 25px; }

/* 25 design px / 22 CSS. Off the preset scale in the other direction:
   `xl` is 24-26px here and `lg` 20px. */
.legal__body h2 {
  font-size: 1.375rem;
  font-weight: 700;
  line-height: 1.2;
}
.legal__body > * + h2 { margin-block-start: 53px; }
.legal__body > h2 + * { margin-block-start: 26px; }
/* ...but a section heading runs straight into its own opening sentence,
   exactly as h3 does: in the export "Your Rights" and "General
   information on data processing" sit one plain line above the copy they
   introduce, baseline to baseline, with nothing added. The 26px above is
   really the gap before a following *subhead*, and only ever lands on one
   because every other h2 here is followed by an h3.

   The Hosting section is the single place the supplied copy leaves a
   blank line between the heading and its first line; it is set tight
   like the other six rather than given a rule of its own. */
.legal__body > h2 + p { margin-block-start: 0; }

/* A block of lines the design keeps together as one unit -- the Impressum's
   § 18 MStV block, the Privacy Policy's contact details. Inside it the copy
   runs on the page's own rhythm, not core's block gap. */
.legal__block > * { margin-block: 0; }
.legal__block > * + * { margin-block-start: 1.5em; }

/* --- Lists -------------------------------------------------------
   Only the Privacy Policy uses these so far. The design sets a list as
   plain body copy with a small dot hung in the margin: text at 30 design
   px / 27 CSS from the column edge, the dot at 12 / 11, and the lines on
   the same 1.5 leading as everything else -- no gap between items and
   none between a list and the line that introduces it.

   The dot is drawn rather than left to ::marker: it is 4px, about half
   what a disc renders at this size, and `outside` gives no control over
   where the marker lands. list-style is dropped so the two cannot both
   paint. */
.legal__body ul {
  margin-block: 0;
  padding-inline-start: 27px;
  list-style: none;
}
.legal__body ul > li { position: relative; }
.legal__body ul > li::before {
  content: "";
  position: absolute;
  /* Back from the text edge, not forward from the column: the li's
     content box starts at the 27px padding edge. 27 - 16 = 11. */
  left: -16px;
  /* Half the 1.5 line box, less half the dot. */
  top: calc(.75em - 2px);
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
}
/* A list follows its lead-in line at plain line rhythm -- the colon and
   the first bullet are one line apart, like any two lines of a
   paragraph. What follows the list keeps the ordinary gap. */
.legal__body > * + ul { margin-block-start: 0; }

/* The § 18 MStV block is set a step smaller than the rest of the page --
   18 design px / 16 CSS, still on 1.5 leading. Its own lead-in heading
   follows the block, not the h2 above it. */
.legal__block--sm { font-size: 1rem; }
.legal__body > .legal__block--sm { margin-block-start: 48px; }
.legal__block--sm > * { margin-block: 0; }
.legal__block--sm > * + * { margin-block-start: 1.5em; }
.legal__block--sm h2 {
  font-size: 1em;
  line-height: inherit;
}

/* --- Privacy Policy ----------------------------------------------
   The one legal page whose links sit inside running sentences ("...at
   the following link: https://...", "...by email at contact@..."). They
   are set in body colour like every other link on the page, so without
   an underline there is nothing at all to mark them -- which is both a
   departure from the design and WCAG 1.4.1. The Impressum has no links
   in prose and keeps its undecorated treatment, hence the page scope.

   The contact block at the top opts back out -- the design draws those as
   plain address lines -- except for the two web addresses in it, which it
   does underline. That is a property of the block and not of the href:
   the mailto: in the running copy below is underlined, the identical one
   in the contact block is not. */
.page--privacy-policy .legal__body a { text-decoration: underline; }
.page--privacy-policy .legal__body .legal__contact a { text-decoration: none; }
.page--privacy-policy .legal__body .legal__contact a[href^="http"] { text-decoration: underline; }
.page--privacy-policy .legal__body .legal__contact a:hover,
.page--privacy-policy .legal__body .legal__contact a:focus-visible { text-decoration: underline; }

@media (max-width: 781px) {
  .legal__inner { padding-block-start: var(--wp--preset--spacing--50); }
  /* The desktop gaps are all roughly one title-height, and the title is
     less than half the size down here -- keep the proportion, not the px. */
  .legal__inner > .legal__body { margin-block-start: var(--wp--preset--spacing--50); }
  .legal__body > * + h2 { margin-block-start: var(--wp--preset--spacing--50); }
  .legal__body > .legal__block--sm { margin-block-start: var(--wp--preset--spacing--50); }
}


/* --- Demo request page ("Talk to a product expert") ----------------
   Measured off "Demo form.png" (1512-wide frame, content 70..1441 =
   1372px) against the theme's 1240px wide container, so every horizontal
   figure and every font-size below is a design pixel scaled by
   1240/1372 = .904 -- the same factor the footer and the legal pages use.

   The design's three columns come to 558 + 140 + 674 = 1372, which is
   504 + 127 + 609 at that scale.
------------------------------------------------------------------*/

/* The one dark page on the site.

   Scoped to the body class core gives a page template, not to the page slug:
   `page--book-a-demo` would work today and break the day someone renames the
   page, and the colour belongs to the template rather than to one post.
   (Note the front-end class is `page-template-demo`; `wp-custom-template-demo`
   is the block editor's own, and only exists inside the editor iframe.) Everything under here is a repaint of components the rest of
   the site draws on white -- the header, the footer and the logo strip are
   shared, so none of it may leak past this selector. */
.page-template-demo {
  background: var(--wp--preset--color--page-deep);
  color: var(--wp--preset--color--base);
}

/* The root block gap drops 24px above <main> and another 24px above the
   footer. This page sets its own space at both ends, so take those two out
   and keep the figures in one place rather than netted off. */
.demo-main, .demo-main + .wp-block-template-part { margin-block-start: 0; }

/* 69 design px of air under the header before the top of the form card, and
   the footer brings its own 80px of padding-top under the last of it. */
.demo { padding-block: 62px 40px; }

/* --- Header and footer, repainted -------------------------------- */

/* The design's header is the page colour exactly, not a tint of it, so the
   translucent fill and its blur are switched off rather than recoloured:
   at 88% over a dark ground the content scrolling underneath shows through
   as a moving stain, which is far more obvious than it is on white. */
.page-template-demo .site-header::before {
  background: var(--wp--preset--color--page-deep);
  backdrop-filter: none;
}
/* logo.svg is a single flat #0E1C1E, so this is a knockout to white rather
   than a colour change -- which is why one filter can serve the wordmark in
   the header, the big one in the footer and every client logo, and why no
   white variant of any of those files has to be kept in step with its
   original. */
.page-template-demo .brand__logo,
.page-template-demo .footer-wordmark,
.logo-carousel--inverted .logo-carousel__logo {
  filter: brightness(0) invert(1);
}

/* :not() on the footer link rule, rather than a louder selector on the badge
   itself: the badge is an <a> inside .site-footer, so anything that repaints
   footer links white repaints a card that is permanently light grey. */
.page-template-demo .site-header .wp-block-navigation a,
.page-template-demo .site-header .nav-secondary a,
.page-template-demo .site-footer,
.page-template-demo .site-footer a:not(.giata-badge) {
  color: var(--wp--preset--color--base);
}
.page-template-demo .site-header .wp-block-navigation a:hover {
  color: var(--wp--preset--color--brand-lilac);
}

/* The mobile overlay is the navigation block's own white panel (core renders
   it has-base-background-color / has-contrast-color from the block's
   attributes), and it lives inside .site-header -- so the rule above paints
   its links white on white and the menu opens apparently empty. Same
   specificity, declared after, so the overlay wins inside itself. */
.page-template-demo .site-header .is-menu-open a,
.page-template-demo .site-header .is-menu-open a:hover {
  color: var(--wp--preset--color--contrast);
}

/* The hamburger is an SVG with fill:currentColor; the overlay it opens stays
   white with dark text (core renders that from the block's own attributes),
   which is legible and identical to every other page. Only the closed-state
   icon sits on the dark header and needs turning. */
.page-template-demo .wp-block-navigation__responsive-container-open {
  color: var(--wp--preset--color--base);
}

/* The gradient-bordered CTA, hollowed out over the dark ground. The
   padding-box fill has to be opaque: a transparent one does not clip the
   border-box layer underneath, it lets it through, and the button fills
   solid with the gradient instead of showing a 2px ring of it. */
.page-template-demo .header-cta .wp-block-button__link,
.page-template-demo .header-cta .wp-block-button__link:hover,
.page-template-demo .header-cta .wp-block-button__link:focus-visible {
  color: var(--wp--preset--color--base);
  background:
    linear-gradient(var(--wp--preset--color--page-deep), var(--wp--preset--color--page-deep)) padding-box,
    var(--cta-border) border-box;
}
.page-template-demo .header-cta .wp-block-button__link:hover,
.page-template-demo .header-cta .wp-block-button__link:focus-visible {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--wp--preset--color--brand-lilac) 22%, transparent);
}

/* Focus rings are drawn in brand purple, which on #2A1944 is very nearly the
   page itself -- the ring disappears exactly where it matters most. */
.page-template-demo :where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline-color: var(--wp--preset--color--brand-lilac);
}
/* ...except inside the white card, where purple reads perfectly well. */
.demo-form :where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline-color: var(--wp--preset--color--brand);
}

/* --- Layout ------------------------------------------------------- */

/* The form column is fixed and the pitch column takes the rest, so the card
   keeps the width its fields were drawn at while the copy absorbs every
   viewport between the breakpoint and the container's own maximum. */
.demo__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 609px;
  column-gap: 127px;
  align-items: start;
}
/* The layout is a block group, so core's flow layout puts a block gap between
   its two children -- which in a grid is a top margin on the form card alone,
   dropping it out of line with the heading beside it. */
.demo__layout > * { margin-block: 0; }

@media (max-width: 1100px) {
  .demo__layout { grid-template-columns: minmax(0, 1fr); row-gap: var(--wp--preset--spacing--60); }
  .demo__panel { max-inline-size: 609px; }
}

/* --- Pitch column ------------------------------------------------- */

/* 14 design px lower than the form card beside it -- the two columns are grid
   siblings and start level, so the offset goes on this one. */
.demo__intro { padding-block-start: 6px; }

.demo__back {
  display: inline-flex; align-items: center; gap: 12px;
  color: inherit; text-decoration: none;
  font-size: var(--wp--preset--font-size--sm); font-weight: 700;
}
.demo__back:hover { color: var(--wp--preset--color--brand-lilac); text-decoration: none; }
.demo__back-arrow {
  inline-size: 14px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

/* 68 design px, and weight 400 against the theme's 800 for an h1 -- this is
   a page title, not the homepage hero, and the design sets it noticeably
   lighter. Measured two ways off the export (ink width against a canvas
   render, and ascender height) which agree on 400 at this size.

   The <br> after "Talk to a" is kept at every width: "Talk to a product"
   fits on one line in this column, so left to wrap on its own the heading
   breaks a word later than the design draws it. */
.demo__title {
  margin: 38px 0 0;
  font-size: clamp(2.375rem, 1.5rem + 2.8vw, 3.8125rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -.01em;
}

/* 16 design px / 14.5 CSS, and the 3.4% that `sm` would add is not free
   here: the design fits "...question or company, so" on the first line with
   13px to spare in a 504px column, and at 15px it no longer fits, so the
   paragraph rewraps a word early against its own export. Same value, and the
   same reason, as .footer-address. */
.demo__lead {
  margin: 9px 0 0;
  font-size: .90625rem;
  line-height: 1.5;
}

.demo__expect-title {
  margin: 20px 0 0;
  font-size: var(--wp--preset--font-size--sm);
  font-weight: 700;
}

.demo__expect { margin: 10px 0 0; padding: 0; list-style: none; }
.demo__expect-item {
  display: flex; align-items: flex-start; gap: 14px;
  font-size: var(--wp--preset--font-size--sm);
  line-height: 1.3;
}
/* 32 design px between one tick and the next. On the gap between items, not
   on every item, so the first one sits where .demo__expect puts it. */
.demo__expect-item + .demo__expect-item { margin-block-start: 7px; }
/* The tick is drawn rather than filed as an asset: it is two shapes and one
   palette colour, and an <img> could not take the colour from the text. */
.demo__check { inline-size: 20px; flex: none; margin-block-start: .1em; }
.demo__check circle { fill: var(--wp--preset--color--brand-lilac); }
.demo__check path {
  fill: none; stroke: var(--wp--preset--color--base); stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}

.demo__trusted {
  margin: 43px 0 0;
  font-size: var(--wp--preset--font-size--sm);
  font-weight: 700;
}

/* The shared strip, narrowed: its own breakpoints read the viewport, which
   in this column is more than twice the space it actually has. */
.demo__intro .logo-carousel { margin-block-start: 22px; }
.logo-carousel--narrow { --logo-carousel-visible: 3; --logo-carousel-step: 6s; }

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

/* The fields, the submit button and the legal line are the dialog's, reused
   wholesale -- same control, same design, same size. Only the card around
   them and the product list are new.

   The dialog declares these three on .contact-modal, so they resolve to
   nothing out here and `border: 1px solid var(--contact-field-border)` would
   fall back to currentColor. Note the border is the #b0c7cf this design
   draws, not the #739499 the dialog is currently built with. */
.demo-form {
  --contact-field-border: #b0c7cf;
  --contact-placeholder: #739499;
  --contact-error: #b32d2e;

  /* Asymmetric, like the dialog's own 60/48/58: the design leaves more air
     above the first legend than beside the fields. */
  padding: 55px 44px 52px;
  border-radius: 16px;
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
}

/* A fieldset is a box with a border and its own padding by default, and the
   legend is pulled into that border. Neither is wanted; the grouping is for
   assistive technology, not for the drawing. */
.demo-form__group { margin: 0; padding: 0; border: 0; min-inline-size: 0; }
.demo-form__group--products { margin-block-start: 40px; }

.demo-form__legend {
  display: block; float: left; inline-size: 100%;
  padding: 0; margin-block-end: 17px;
  /* 19 design px / 17.25 CSS -- between the `sm` and `lg` presets, and both
     legends measure it independently. */
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.3;
}
/* float above needs clearing, and is only there because a <legend> ignores
   width unless it is taken out of flow -- so a long question wraps to the
   fieldset's width instead of the shrink-to-fit width of its own text. */
.demo-form__legend + * { clear: both; }

/* --- Product checklist -------------------------------------------- */

/* One bordered box with hairlines between the rows, not seven bordered
   rows: adjacent borders would double to 2px against the box's own 1px. */
.demo-products {
  margin: 0; padding: 0; list-style: none;
  border: 1px solid var(--contact-field-border);
  border-radius: 4px;
}
/* The divider goes on the row rather than the <li> so that border-box sizing
   counts it inside the 72px: on the <li> it would add to it, and seven rows
   would run 6px past the height the design draws. */
.demo-products__item + .demo-products__item .demo-product {
  border-top: 1px solid var(--contact-field-border);
}

/* The whole row is the label, so the text is a hit target for the checkbox
   at the far end of it -- 500px away, which is a long way to ask anyone to
   aim at an 18px box. */
.demo-product {
  display: flex; align-items: center; gap: 16px;
  min-block-size: 72px;
  padding: 12px 14px 12px 15px;
  cursor: pointer;
}
.demo-product__text { flex: 1; min-inline-size: 0; }
.demo-product__title {
  display: block;
  font-size: .875rem;
  font-weight: 700;
  line-height: 1.3;
}
/* 14 design px / 12.7 CSS -- three of the seven descriptions measure it to
   the same tenth. The colour is the design's own; the theme has no token
   between `muted-ink` and the field grey, and this is neither. */
.demo-product__desc {
  display: block;
  margin-block-start: 5px;
  color: #4c6b72;
  font-size: .8125rem;
  line-height: 1.35;
}

.demo-product__box {
  appearance: none;
  flex: none;
  inline-size: 18px; block-size: 18px;
  margin: 0;
  border: 1px solid var(--contact-field-border);
  border-radius: 4px;
  background: var(--wp--preset--color--base);
  cursor: pointer;
}
.demo-product__box:checked {
  border-color: var(--wp--preset--color--brand);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' fill='none'%3E%3Cpath d='M1 5.2 4.3 8.5 11 1.5' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center no-repeat,
    var(--wp--preset--color--brand);
}
.demo-product:hover .demo-product__box { border-color: var(--wp--preset--color--brand); }

/* The dialog's own figures are 48px and 24px; this design sets both a little
   tighter, and the card is not a dialog with a title above it. */
.demo-form .contact-form__submit { margin-block-start: 44px; }
.demo-form .contact-form__legal  { margin-block-start: 18px; }

/* The dialog centres its success message in a box of its own; here it
   replaces a form that filled the card, so it gets the card's own rhythm. */
.demo-form .contact-form__success h2 {
  margin: 0 0 12px;
  font-size: var(--wp--preset--font-size--xl);
  font-weight: 500;
}
.demo-form .contact-form__success p { margin: 0; color: var(--wp--preset--color--muted-ink); }

@media (max-width: 720px) {
  .demo-form { padding: 28px 20px; border-radius: 12px; }
  .demo-form__group--products { margin-block-start: 34px; }
  .demo-product { padding-inline: 14px; gap: 12px; }
  .demo-form .contact-form__submit { inline-size: 100%; }
}
