@charset "UTF-8";

/**
 * Homepage (front page) — 2026 redesign.
 *
 * Scoped to #page-home so nothing here can leak into other templates. Layout
 * comes from Foundation's XY grid (.grid-container/.grid-x/.cell) and Tailwind
 * spacing utilities, same as the rest of the v2 templates; this file only adds
 * the page-specific surfaces, cards and the two bits of motion.
 *
 * Colors are the design-system tokens the Figma file draws from:
 *   baltic-15 #C3F8FB  baltic-20 #8FE3E8  baltic-50 #0A6190  baltic-60 #014063
 *   baltic-70 #081E2B
 *   neutral-55 #535B66 neutral-75 #151E29
 * Sampled one-offs from the Figma frames are noted inline.
 */

/* The site-wide face set registers SyneNeo Medium alone (assets_v2 app.scss), so
   the hero's accent word brings its own Bold. Registered under its own family
   name — the one the theme's font stylesheet already uses — rather than as
   syne-neo/700, so adding it cannot silently re-weight anything else that asks
   for a bold heading on this page. */
@font-face {
  font-family: syne-neo-bold;
  src: url("../../../assets_v2/fonts/syne-neo/SyneNeo-Bold.woff2") format("woff2"),
       url("../../../assets_v2/fonts/syne-neo/SyneNeo-Bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

#page-home {
  /* The hero's flat dark, and the color its backdrop fades out to. It is also
     the statement band's own first stop, so the hero hands off to the section
     below it with no step between the two.
     Pure black, and everything that paints the hero's field has to agree with
     it: the feathering gradients, the scroll veil, the bottom fade, and the
     WebGL composite's base ink (assets/hero-graph.js, INK). Where any of them
     sat a few values off, the canvas read as a panel on the band rather than as
     the band itself. */
  --hp-ink: #000000;
  --hp-ink-partners: #06151F;
  --hp-baltic-15: #C3F8FB;
  --hp-baltic-20: #8FE3E8;
  --hp-baltic-50: #0A6190;
  --hp-baltic-60: #014063;
  --hp-baltic-70: #081E2B;
  --hp-text: #151E29;
  --hp-muted: #535B66;
  --hp-rule: #D5D9DE;
  /* FAQ container border and dividers, sampled from the frame as rgb(187,190,195) */
  --hp-rule-strong: #BBBEC3;
  --hp-accent-word: #125B89;  /* "trust" / "smarter" — sampled from Figma */
  /* Closing-CTA button fill. The frame samples #1E6B94, but the label the design
     asks for on top of it (baltic-20) lands at 3.99:1 there — under AA for text
     this size. A shade down to the page's accent blue carries it to 4.97:1 and
     is imperceptible against the frame. */
  --hp-cta: #125B89;
  --hp-cta-hover: #02507B;
  /* Motion's spring(duration: 2, bounce: 0), sampled — see .hp-hero__word. */
  --hp-spring: linear(0, 0.0322, 0.1078, 0.204, 0.3064, 0.4063, 0.4991, 0.5823, 0.6551, 0.7177, 0.7708, 0.8153, 0.8522, 0.8827, 0.9077, 0.928, 0.9445, 0.9578, 0.9685, 0.9771, 0.984, 0.9895, 0.9938, 0.9973, 1);
  --hp-card-radius: 16px;
  --hp-shadow: 0 18px 40px rgba(6, 21, 31, 0.10);
  /* Shared by .hp-story and the active .hp-tab — the frame gives them the same
     treatment, and the tab carries no border.
     Fitted to the frame's measured edge profile rather than guessed: the falloff
     reaches ~7% darkness and dies out within ~7-12px, so the blur has to stay
     tight. A wider, fainter shadow spreads that peak too thin to read at all,
     which is what made the active tab look flat. */
  --hp-card-shadow: 0 5px 18px rgba(6, 21, 31, 0.13);

  position: relative;
  overflow-x: clip;
}

/* Sections own their own vertical rhythm on this page. */
#page-home > section,
#page-home .hp-section {
  padding-top: 6rem;
  padding-bottom: 6rem;
}

@media screen and (max-width: 1023px) {
  #page-home > section,
  #page-home .hp-section {
    padding-top: 3.5rem;
    padding-bottom: 3.5rem;
  }

  /* Each of these sets its own, larger, rhythm further down the file. Listed by
     class alone they tie on specificity and lose on source order — which they
     silently did, leaving the statement band 10.5rem of padding on a phone. The
     type selector is what makes the mobile value stick. Sections that mean to
     keep their own (the hero's zero bottom, the partners stage) are not here. */
  #page-home > section.hp-statement,
  #page-home > section.hp-capabilities,
  #page-home > section.hp-customers,
  #page-home > section.hp-graphrag,
  #page-home > section.hp-news,
  #page-home > section.hp-faq {
    padding-top: 3.5rem;
    padding-bottom: 3.5rem;
  }
}

/* ── shared type ─────────────────────────────────────────────────────────── */

#page-home .hp-h2 {
  font-size: 48px;
  line-height: 1.14;
  letter-spacing: -0.01em;
  margin: 0;
}

#page-home .hp-h3 {
  font-size: 48px;
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin: 0;
}

/* The enterprise card's heading runs one step larger than the other h3s. It has
   to be declared here, with the rest of the base type, rather than down in the
   card's own block: the breakpoints below match it on the same specificity, so
   a later rule would win at every width and the step-downs would never apply.
   That is what used to pin it at 55px on a phone, where "Enterprise-" alone is
   wider than the card. */
#page-home .hp-enterprise__copy .hp-h3 { font-size: 55px; }

#page-home .hp-lede {
  font-size: 20px;
  line-height: 1.5;
}

#page-home .hp-body {
  font-size: 16px;
  line-height: 1.55;
  color: var(--hp-muted);
}

@media screen and (max-width: 1023px) {
  #page-home .hp-h2 { font-size: 40px; }
  #page-home .hp-h3 { font-size: 34px; }
  #page-home .hp-enterprise__copy .hp-h3 { font-size: 38px; }
  #page-home .hp-story__text { font-size: 20px; }
  #page-home .hp-news-card__title { font-size: 21px; }
}

@media screen and (max-width: 639px) {
  #page-home .hp-h2 { font-size: 32px; }
  #page-home .hp-h3 { font-size: 27px; }
  #page-home .hp-enterprise__copy .hp-h3 { font-size: 30px; }
  #page-home .hp-lede { font-size: 18px; }
  #page-home .hp-story__text { font-size: 18px; }
  #page-home .hp-faq__q { font-size: 17px; }
}

#page-home .hp-on-dark,
#page-home .hp-on-dark .hp-h2,
#page-home .hp-on-dark .hp-h3 {
  color: #fff;
}

#page-home .hp-on-dark .hp-body {
  color: #C4C8CD;
}

/* Light surfaces inside a dark band opt back out of the white type. Without
   this, headings on the white cards render white-on-white. */
#page-home .hp-on-dark .hp-card,
#page-home .hp-on-dark .hp-card .hp-h2,
#page-home .hp-on-dark .hp-card .hp-h3,
#page-home .hp-on-dark .hp-news-card,
#page-home .hp-on-dark .hp-news-card .hp-h3,
#page-home .hp-on-dark .hp-story,
#page-home .hp-on-dark .hp-story .hp-h3 {
  color: var(--hp-text);
}

#page-home .hp-on-dark .hp-card .hp-body,
#page-home .hp-on-dark .hp-news-card .hp-body,
#page-home .hp-on-dark .hp-story .hp-body {
  color: var(--hp-muted);
}

#page-home .hp-on-dark .hp-card .hp-link,
#page-home .hp-on-dark .hp-news-card .hp-link,
#page-home .hp-on-dark .hp-story .hp-link {
  color: var(--hp-baltic-50);
}

#page-home .hp-on-dark .hp-card .hp-link:hover,
#page-home .hp-on-dark .hp-news-card .hp-link:hover,
#page-home .hp-on-dark .hp-story .hp-link:hover {
  color: var(--hp-cta-hover);
}

/* Arrow link — "Learn how ›", "Link to full story ›", "Read more ›" */
#page-home .hp-link {
  display: inline-block;
  font-size: 16px;
  font-weight: 700;
  color: var(--hp-baltic-50);
  text-decoration: none;
}

#page-home .hp-link::after {
  content: "\203A";
  display: inline-block;
  margin-left: 0.4em;
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

#page-home .hp-link:hover,
#page-home .hp-link:focus {
  color: var(--hp-cta-hover);
  text-decoration: none;
}

#page-home .hp-link:hover::after,
#page-home .hp-link:focus::after {
  transform: translateX(4px);
}

#page-home .hp-on-dark .hp-link,
#page-home .hp-link--light {
  color: var(--hp-baltic-20);
}

#page-home .hp-on-dark .hp-link:hover,
#page-home .hp-link--light:hover {
  color: #fff;
}

#page-home .hp-inline-link {
  color: var(--hp-baltic-50);
  text-decoration: none;
}

#page-home .hp-inline-link:hover { text-decoration: underline; }

#page-home .hp-on-dark .hp-inline-link { color: var(--hp-baltic-20); }

#page-home .hp-eyebrow {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fff;
  margin: 0 0 0.5rem;
}

/* Numbers read as one left-to-right ramp across a row, as in the frame, cut from
   the base gradient the design works from:
   linear-gradient(236.53deg, #84DFE5 16.45%, #0D5685 71.89%).
   Each figure carries its own SEGMENT of that ramp (--ramp-from/--ramp-to, set
   per cell in PHP) rather than sharing one viewport-anchored gradient.
   background-attachment: fixed would do it in one line, but it resolves against
   the viewport and silently collapses to transparent text the moment ANY
   ancestor gets a transform, filter or will-change — so the figures would vanish
   under a parent animation. This is equivalent and can't break that way. */
#page-home .hp-figure {
  display: block;

  /* Syne for the numbers, as in the frame — the same family the headings use,
     spelled out rather than taken from --wp--preset--font-family--syne-neo so
     this file keeps reading the same way as the hero's accent above.

     The weight is pinned because the site registers Syne as a single face at
     normal: inherit a bold from anywhere above and the browser would synthesize
     one, and a faked bold of a display face at 56px is obvious. */
  font-family: syne-neo, "Helvetica Neue", helvetica, roboto, arial, sans-serif;
  font-weight: 400;
  font-size: 56px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  /* Static state (and what reduced-motion keeps): the figure's own segment of
     the row ramp, at the base gradient's angle. 236.53deg puts 0% at the
     top-right, so the segment's light end is the one that leads. */
  background-image: linear-gradient(
    236.53deg,
    var(--ramp-to, #84DFE5) 0%,
    var(--ramp-from, #0D5685) 100%
  );
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Both layers advance on one timeline.
   Layer 2 (the ramp): background-position in percent resolves against
   (box width − image width), so with a 200% image, 200% is exactly one image
   width — the cycle lands on an identical frame and the loop is invisible.
   Layer 1 (the specular): its center walks a closed ellipse and returns to the
   start, so it never reverses direction or snaps back. */
@keyframes hp-figure-flow {
  0%   { background-position: 8.0% 50.0%, 0% 0; }
  12.5%   { background-position: 20.3% 21.7%, 25% 0; }
  25%   { background-position: 50.0% 10.0%, 50% 0; }
  37.5%   { background-position: 79.7% 21.7%, 75% 0; }
  50%   { background-position: 92.0% 50.0%, 100% 0; }
  62.5%   { background-position: 79.7% 78.3%, 125% 0; }
  75%   { background-position: 50.0% 90.0%, 150% 0; }
  87.5%   { background-position: 20.3% 78.3%, 175% 0; }
  100%   { background-position: 8.0% 50.0%, 200% 0; }
}

@media (prefers-reduced-motion: no-preference) {
  /* In motion each figure cycles the WHOLE base range rather than its own
     narrow slice of it — a per-figure segment spans too little contrast for the
     movement to register. The stops below are the design's own: the base
     gradient's dark end and the steps up from it, one step past that dark end,
     and a plateau where the ramp turns rather than a bright peak — the only
     specular is the radial layer above.
     TWO identical cycles sit across the 200% tile, so the visible window (always
     half the tile) holds exactly one complete cycle. That is what keeps the
     numbers legible: every figure always carries its dark end as well as its
     light one, so no phase can leave a number washed out on a white card.
     The tile also opens and closes on the same color, so it repeats seamlessly
     and the travel never restarts. `linear` is deliberate — any easing puts the
     pulse back in. */
  #page-home .hp-figure {
    background-image:
      /* A soft specular blob whose center travels a closed ellipse — this is the
         "radial" part. Light rolls across and around the glyphs rather than
         sliding straight past them, which is what reads as smooth. It is tinted
         baltic-15 rather than white: a white overlay would lift the ramp's dark
         end and eat the contrast the two-cycle trick is there to protect. */
      radial-gradient(
        ellipse at center,
        rgba(195, 248, 251, 0.42) 0%,
        rgba(195, 248, 251, 0.18) 38%,
        rgba(195, 248, 251, 0) 68%
      ),
      linear-gradient(
      100deg,
      #0B4971 0%,
      #0D5685 7%,
      #307EA1 15%,
      #57ABC1 21%,
      #57ABC1 30%,
      #307EA1 36%,
      #0D5685 43%,
      #0B4971 50%,
      #0D5685 57%,
      #307EA1 65%,
      #57ABC1 71%,
      #57ABC1 80%,
      #307EA1 86%,
      #0D5685 93%,
      #0B4971 100%
    );
    background-size: 62% 180%, 200% 100%;
    background-repeat: no-repeat, repeat-x;
    /* One cycle moves the tile a full image width, i.e. two color cycles — so a
       given glyph catches the light band every --flow-duration/2. At 16s that is
       roughly every 8s: present, but a slow drift rather than a throb. Tune the
       whole effect from this one value.
       --flow-delay is negative and scales with the figure's index, so the row is
       already mid-flow on first paint and the numbers stay out of lockstep —
       and the phase still climbs left-to-right, keeping the ramp's reading.
       Both layers share one period, so the whole effect has a single heartbeat. */
    animation: hp-figure-flow var(--flow-duration, 16s) linear var(--flow-delay, 0s) infinite;
  }
}

#page-home .hp-figure sub,
#page-home .hp-figure .hp-figure__unit {
  font-size: 0.62em;
  vertical-align: baseline;
  bottom: 0;
}

#page-home .hp-figure__label {
  display: block;
  margin-top: 0.5rem;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--hp-text);
}

@media screen and (max-width: 639px) {
  #page-home .hp-figure { font-size: 40px; }
}

/* Below 640px the two enterprise stats stack (small-12), and the row carries
   only grid-margin-x — so without this they would sit flush against each
   other. */
@media screen and (max-width: 639px) {
  #page-home .hp-enterprise__stats .cell + .cell { margin-top: 2rem; }
}

/* ── white card ──────────────────────────────────────────────────────────── */

#page-home .hp-card {
  position: relative;
  height: 100%;
  background: #fff;
  border-radius: var(--hp-card-radius);
  box-shadow: var(--hp-shadow);
  overflow: hidden;
}

#page-home .hp-card__body { padding: 2.5rem; }

@media screen and (max-width: 639px) {
  #page-home .hp-card__body { padding: 1.5rem; }
}

#page-home .hp-accent { color: var(--hp-accent-word); }

/* ── grids with their own gutter ──────────────────────────────────────────── */

/* Foundation bakes its 3rem gutter into the cell WIDTH as well as into the
   margins, so a row that wants a different one has to restate both — moving the
   margins alone overflows it. --hp-gutter carries the value, set per grid; only
   the width classes these grids actually use need an entry here. */
#page-home .hp-grid.grid-margin-x {
  margin-left: calc(var(--hp-gutter) / -2);
  margin-right: calc(var(--hp-gutter) / -2);
}

#page-home .hp-grid.grid-margin-x > .cell {
  width: calc(100% - var(--hp-gutter));
  margin-left: calc(var(--hp-gutter) / 2);
  margin-right: calc(var(--hp-gutter) / 2);
}

@media print, screen and (min-width: 48em) {
  #page-home .hp-grid.grid-margin-x > .medium-6 { width: calc(50% - var(--hp-gutter)); }
}

@media print, screen and (min-width: 64em) {
  #page-home .hp-grid.grid-margin-x > .large-4 { width: calc(33.3333% - var(--hp-gutter)); }
}

#page-home .hp-grid.grid-margin-y {
  margin-top: calc(var(--hp-gutter) / -2);
  margin-bottom: calc(var(--hp-gutter) / -2);
}

#page-home .hp-grid.grid-margin-y > .cell {
  margin-top: calc(var(--hp-gutter) / 2);
  margin-bottom: calc(var(--hp-gutter) / 2);
}

/* ═══════════════════════════════════════════════════════════════════════════
   1. Hero
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-hero {
  position: relative;
  background-color: var(--hp-ink);
  padding-bottom: 0;
  overflow: hidden;

  /*
   * How much height the hero gives back on a short screen.
   *
   * The logo strip is the hero's foot, and at full size the section runs about
   * 925px. With the announcement bar above it that needs ~975px of screen, so
   * every laptop shorter than that — a 900px MacBook included — cut the strip in
   * half along the bottom of the window, which reads as broken rather than as
   * "there is more below".
   *
   * This is the shortfall, zero on anything tall enough and growing point for
   * point as the window shrinks. The three paddings below share it out by
   * weights that sum to 1, so the hero loses exactly this much and no more. It
   * is capped: past 240px there is no slack left to take and the copy would
   * start colliding with the strip.
   *
   * Zero below 1024px, where the hero is already short enough and the mobile
   * rules set their own padding.
   */
  --hp-hero-squeeze: 0px;
  padding-top: calc(6rem - var(--hp-hero-squeeze) * 0.4);
}

@media screen and (min-width: 1024px) {
  #page-home .hp-hero { --hp-hero-squeeze: clamp(0px, 980px - 100svh, 240px); }
}

/* The network render sits flush right; a gradient fades it into the flat dark
   so the headline always lands on an even surface. */
/* The backdrop is the design's own source art (720x544 in the Figma file),
   upscaled once to 1440x1088 with sharpening and fine grain. Its aspect (1.32)
   is deliberately matched to this layer's box, so `cover` barely crops and the
   image is never blown up much beyond 1:1 — the previous version cropped the
   right 47% of a PDF re-render and stretched ~340px of real detail across
   1000px, which is where the blur came from. */
#page-home .hp-hero__bg {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 70%;
  background-repeat: no-repeat;
  background-position: center top;
  background-size: cover;
  z-index: 0;
}

/* The still is opt-in, not opt-out: it is only painted when the animated
   backdrop is not going to run, so the 297KB image is never fetched on the
   loads that would immediately cover it. --still is set by the inline probe in
   section-hero.php (reduced motion), by hero-graph.js (no WebGL), and by the
   <noscript> block there (no JS at all). */
#page-home .hp-hero__bg--still {
  background-image: url("hero-network.jpg");
}

/* The orbiting graph (assets/hero-graph.js) renders into this layer, under
   the feathering gradient. It is the default backdrop; the still below is what
   a browser gets when this cannot run. */
#page-home .hp-hero__canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* Reduced motion keeps the still frame rather than a looping backdrop. */
@media (prefers-reduced-motion: reduce) {
  #page-home .hp-hero__canvas { display: none; }
}

/* When the animated loop is present it covers the still, which stays as the
   video's poster and as the fallback if the file is absent or cannot play. */
#page-home .hp-hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: left top;
  border: 0;
  pointer-events: none;
}

/* Reduced motion keeps the still frame rather than a looping backdrop. */
@media (prefers-reduced-motion: reduce) {
  #page-home .hp-hero__video { display: none; }
}

/* Feather the render's left edge into the flat base so the headline always sits
   on an even surface, and knock back its bottom so the logo strip stays legible.
   Sits above the video, so the feathering applies to both. */
#page-home .hp-hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  /* background-image, not the `background` shorthand — the shorthand would reset
     background-color, which is what carries the scroll veil above. */
  background-image:
    linear-gradient(
      to top,
      var(--hp-ink) 0%,
      rgba(0, 0, 0, 0.55) 14%,
      rgba(0, 0, 0, 0) 34%
    ),
    linear-gradient(
      to right,
      var(--hp-ink) 0%,
      rgba(0, 0, 0, 0.78) 12%,
      rgba(0, 0, 0, 0.22) 34%,
      rgba(0, 0, 0, 0) 58%
    );
}

/* The backdrop layer is clipped by the section, which left a hard horizontal cut
   across the render at the hero's foot. This dissolves the whole bottom band into
   black — the color the statement below opens on — so the two sections meet
   with no seam at all.
   z-index 0 with the content at 1: the fade is a sibling that comes after
   .hp-hero__bg in the DOM, so it paints over the render but under the copy and
   the logo strip. */
#page-home .hp-hero::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 300px;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0%,
    rgba(0, 0, 0, 0.45) 38%,
    rgba(0, 0, 0, 0.85) 72%,
    var(--hp-ink) 100%
  );
}

#page-home .hp-hero > .grid-container,
#page-home .hp-hero__proof {
  position: relative;
  z-index: 1;
}

/* Parallax, take two.
   The first version moved the backdrop AND lifted and faded the copy. Fading a
   headline the moment you scroll reads as cheap, and translate+scale together on
   the image made it look like it was sliding rather than sitting behind.
   So: the copy is left completely alone — it stays sharp and stable — and the
   only motion is the backdrop lagging behind the scroll, which is what actually
   produces depth. The layer is given vertical headroom so it can drift without
   ever exposing its own edge, and there is no scale at all.
   A veil deepens as the hero leaves, so it settles into the next band instead of
   simply scrolling off. --hero-p is set by the section's own rAF handler. */
@media (prefers-reduced-motion: no-preference) {
  #page-home .hp-hero__bg {
    /* Headroom must exceed the drift, or the layer's own top edge enters frame at
       full scroll: 16% of the hero is ~121px against a 104px travel. */
    top: -16%;
    height: 132%;
    transform: translate3d(0, calc(var(--hero-p, 0) * 104px), 0);
    will-change: transform;
  }

  #page-home .hp-hero__bg::after {
    background-color: rgba(0, 0, 0, calc(var(--hero-p, 0) * 0.55));
  }
}

#page-home .hp-hero__copy {
  padding:
    calc(4.5rem - var(--hp-hero-squeeze, 0px) * 0.2) 0
    calc(11rem - var(--hp-hero-squeeze, 0px) * 0.4);
}

#page-home .hp-hero h1 {
  font-size: 62px;
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: #fff;
  margin: 0;
}

/* Its own line, so the words the rotator cycles through can differ in length
   without ever reflowing the two lines above. fit-content is what makes the
   gradient read the same on every one of them: as a full-width block the box
   would be the whole column, and each word would sample only as much of the ramp
   as its own length happened to cover. */
#page-home .hp-hero__accent {
  display: block;
  width: fit-content;
  text-transform: uppercase;
  font-family: syne-neo-bold, syne-neo, "Helvetica Neue", helvetica, roboto, arial, sans-serif;
  font-weight: 700;
  background-image: linear-gradient(77.19deg, #30839D 1.78%, #8FE3E8 60.2%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Split-text entrance. The headline arrives a word at a time on load, and the
   accent word replays the same arrival every time the rotator swaps it.
   Split by WORD, not by character: it keeps the headline readable to a screen
   reader, leaves kerning intact inside each word, and lets the accent keep the
   single gradient box its fill is clipped to.
   Nothing is hidden until the script has split the line — the pre-animation
   state rides on .is-arriving, so a headline that never gets scripted is simply
   visible rather than stuck at opacity 0. */
#page-home .hp-hero__word { display: inline-block; }

/* Matches Motion's js-split-text example: opacity 0 -> 1 and y 10px -> 0, a
   spring of duration 2 with bounce 0, staggered 0.05s.
   bounce: 0 is a critically damped spring, x(t) = 1 - (1 + wt)e^(-wt), which no
   cubic-bezier reproduces — so it is sampled into linear() instead. Motion takes
   `duration` as the time to settle (~99%), which puts w at 3.3192 over the 2s;
   the curve below is that function at 24 points. It looks long on paper, but the
   travel is done in about a second and the rest is the tail settling out. */
#page-home .hp-hero__word.is-arriving,
#page-home .hp-hero__accent.is-arriving {
  animation: hp-word-arrive 2s var(--hp-spring) both;
  will-change: transform, opacity;
}

#page-home .hp-hero__accent.is-leaving {
  animation: hp-word-leave 0.4s ease-in both;
  will-change: transform, opacity;
}

@keyframes hp-word-arrive {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* The same arrival, for the accent words further down the page — "knowledge
   layer" in the statement and "smarter" / "trust" on the capability cards.
   Deliberately the hero's own keyframes and spring rather than a second curve,
   so the reveals down the page read as one idea. accent-arrive.js splits the
   phrase and swaps .is-armed for .is-arriving as each comes into view; unlike
   the hero these are hidden first, so the pre-animation state is a class of its
   own rather than something the keyframe carries. */
#page-home .hp-word { display: inline-block; }
#page-home .hp-word.is-armed { opacity: 0; }

#page-home .hp-word.is-arriving {
  animation: hp-word-arrive 2s var(--hp-spring) both;
  will-change: transform, opacity;
}

/* An inline-block is an atomic inline, so the link's own hover underline stops
   being drawn through its words once they are split. Put it back on them. */
#page-home .hp-inline-link:hover .hp-word { text-decoration: underline; }

/* The example is a one-shot entrance and has no exit; the rotator needs one, so
   the outgoing word drops away the way the next one arrives from. */
@keyframes hp-word-leave {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateY(-6px); }
}

#page-home .hp-hero__sub {
  margin: 1.25rem 0 0;
  font-size: 20px;
  font-weight: 700;
  color: #fff;
}

#page-home .hp-hero__actions {
  display: flex;
  align-items: center;
  gap: 1.75rem;
  flex-wrap: wrap;
  margin-top: 3rem;
}

#page-home .hp-hero__actions .button { margin-bottom: 0; }

#page-home .hp-hero__proof {
  padding-bottom: 3rem;
  text-align: center;
}

#page-home .hp-hero__proof-label {
  margin: 0 0 0.5rem;
  font-size: 16px;
  font-weight: 700;
  color: #fff;
}

@media screen and (max-width: 1023px) {
  #page-home .hp-hero h1 { font-size: 44px; }
  #page-home .hp-hero__copy { padding: 3rem 0 4rem; }
  /* No parallax drift on small screens: the layer fills the frame instead. */
  #page-home .hp-hero__bg {
    top: 0;
    height: 100%;
    width: 100%;
    background-position: right top;
    transform: none;
  }
  #page-home .hp-hero__bg::after {
    background: linear-gradient(
      170deg,
      rgba(0, 0, 0, 0.86) 0%,
      rgba(0, 0, 0, 0.62) 45%,
      rgba(0, 0, 0, 0.92) 100%
    );
  }
}

@media screen and (max-width: 639px) {
  #page-home .hp-hero h1 { font-size: 36px; }
  #page-home .hp-hero__actions { gap: 1rem; margin-top: 2rem; }
}

/* Page-scoped CTA fill so the hero and closing CTA match the Figma blue while
   still using the design-system .button component for size/shape/focus. */
/* The closing CTA alone — the hero's button is the plain design-system one.
   baltic-20 is this file's name for the #8FE3E8 the design calls for; held
   across hover too, so the label does not flip to white under the cursor. */
#page-home .hp-cta.button {
  background-color: var(--hp-cta);
  color: #fff;
}

#page-home .hp-cta.button:hover,
#page-home .hp-cta.button:focus {
  background-color: var(--hp-cta-hover);
  color: #fff;
}

#page-home .hp-cta-text {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 16px;
  font-weight: 700;
  color: var(--hp-baltic-20);
  text-decoration: none;
}

#page-home .hp-cta-text:hover,
#page-home .hp-cta-text:focus {
  color: #fff;
  text-decoration: none;
}

#page-home .hp-cta-text svg { transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); }
#page-home .hp-cta-text:hover svg { transform: translateX(4px); }

/* ── logo marquee ────────────────────────────────────────────────────────── */

/* Ambient, not interactive: the strip drifts right-to-left on its own at a rate
   slow enough to read every mark, and there is nothing to hover.
 *
 * That rate is the hero globe's, so the two ambient motions in this section read
 * as one system instead of racing each other. Derivation at the design frame
 * (1440x900): hero-graph.js frames the sphere on the still's own, which puts its
 * silhouette at a 791px radius, and it turns once per 180s (SETTINGS.period).
 * The apparent speed of the mesh you can actually see is ~24px/s — measured by
 * projecting surface points through the rig's own matrices, not by treating the
 * disc as flat, because the visible part sits off the sub-camera point and is
 * foreshortened (the flat-disc figure, 28px/s, is the fastest point rather than
 * a typical one). One set of 20 marks is 2734px (each mark at its own width,
 * 3rem apart), so 2734/24 ~= 114s. Re-measure if a mark is added, removed or
 * resized — the set is no longer a count times one fixed width.
 *
 * The globe scales with the hero and the strip does not, so the two drift apart
 * by ~15% between a short window and a tall one. That is inside what reads as
 * the same speed; anything tighter would need the duration published from JS,
 * and the strip has to keep moving when the graph never runs at all.
 */
#page-home .logos-scrolled {
  --hp-marquee-duration: 114s;
  --hp-logo-height: 2.4rem;

  width: 100%;
  overflow: hidden;
}

#page-home .logos-scrolled__track {
  display: flex;
  align-items: center;
  width: max-content;

  /* The track is the only thing that moves. Promoting it keeps the eighty
     filtered marks rasterized once, so a frame is a layer offset instead of
     re-running brightness/invert over every image. */
  will-change: transform;
  animation: hp-logo-scroll var(--hp-marquee-duration, 64s) linear infinite;
}

#page-home .logos-scrolled__item {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  height: 5rem;
  margin-right: var(--hp-logo-gap, 3rem);
}

/* An item is only as wide as the mark in it, so the space between any two marks
   is the margin and nothing else. A fixed item width padded the narrow ones —
   the BT and BMW roundels carried 38px of ink in a 109px box — and those 70px
   of dead air read as a hole in the strip beside a wordmark that filled its box
   edge to edge. Gaps ran 52px to 117px; now every one of them is 3rem.

   Height is set outright rather than left to a percentage cap: several of these
   files carry a viewBox and no width or height, so they have a ratio but no
   intrinsic size, and with height:auto they resolved to nothing at all. A
   definite height plus the ratio always gives a width. max-width then holds the
   long wordmarks, and object-fit keeps them in proportion when it bites.

   --logo-scale trims the height and the cap together, so it takes the box down
   with the ink and the even spacing survives it. */
#page-home .logos-scrolled__item img {
  width: auto;
  height: calc(var(--hp-logo-height, 2.4rem) * var(--logo-scale, 1));
  max-width: calc(var(--hp-logo-width, 6.8rem) * var(--logo-scale, 1));
  object-fit: contain;
}

/* Figma shows the hero strip as flat white marks. brightness(0) crushes every
   logo — SVG or PNG, color or not — to black, then invert lifts it to pure
   white, which is the only filter chain that works for all of them. */
#page-home .logos-scrolled--white .logos-scrolled__item img {
  filter: brightness(0) invert(1);
  opacity: 0.92;
}

/* Advance by exactly one copy of the set. Because the gap lives on the item as
   margin-right (not as flex `gap`), every item advances width+margin and the
   set width is an exact fraction of the track — so the loop never jumps. */
@keyframes hp-logo-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-100% / var(--autoscroller-loops, 4)), 0, 0); }
}

@media screen and (max-width: 767px) {
  #page-home .logos-scrolled { --hp-logo-width: 5.6rem; }
}

@media (prefers-reduced-motion: reduce) {
  #page-home .logos-scrolled__track { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   2. Statement — "Context, memory, and reasoning"
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-statement {
  padding-top: 10.5rem;
  padding-bottom: 10.5rem;
  /* Opens on the hero's own ink so the junction is one color, and ramps to the
     frame's blue from there. */
  background: linear-gradient(180deg, var(--hp-ink) 0%, #041823 100%);
  text-align: center;
}

/* Half the h2, which is the relationship the frame draws and what sets the four
   lines it breaks into against the 36rem measure. */
#page-home .hp-statement .hp-lede {
  max-width: 36rem;
  margin: 1.5rem auto 0;
  font-size: 24px;
  color: #E6E9EE;
}

@media screen and (max-width: 639px) {
  #page-home .hp-statement .hp-lede { font-size: 19px; }
}

#page-home .hp-statement .hp-link { margin-top: 2.5rem; }

/* ═══════════════════════════════════════════════════════════════════════════
   3. Capabilities — "Graphs & AI drive better decisions with built-in context"
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-capabilities {
  padding-top: 5.75rem;
  padding-bottom: 5.75rem;
  background: linear-gradient(188deg, #014063 13.08%, #081E2B 60.58%);
}

#page-home .hp-capabilities .hp-h2 {
  max-width: 54rem;
  margin: 0 auto;
  text-align: center;
}

/* Foundation's gutter is 3rem; the frame sets the cards 24px apart. The width
   calc is part of the gutter, so the classes this grid actually uses have to be
   restated with it — changing the margins alone would overflow the row. */
#page-home .hp-capabilities__grid { --hp-gutter: 24px; }

/* Section entrance: the heading, then each card, fades up in turn as the band
   reaches the viewport. Measured off the reference recording — each piece takes
   about 0.8s, they start about 0.2s apart, and the curve is a plain ease-out
   (roughly half the distance covered in the first third of the time).
   The pre-animation state rides on .is-armed, which the script adds, so a band
   that never gets scripted is simply visible rather than stuck at opacity 0. */
#page-home .hp-capabilities.is-armed .hp-reveal {
  opacity: 0;
  transform: translateY(32px);
}

#page-home .hp-capabilities.is-revealed .hp-reveal {
  animation: hp-reveal-up 0.8s ease-out var(--reveal-delay, 0s) both;
}

@keyframes hp-reveal-up {
  from { opacity: 0; transform: translateY(32px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  #page-home .hp-capabilities .hp-reveal {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

/* The heading clears the cards by 4.25rem, of which the cell's half-gutter is a
   part — so the row's own margin has to absorb what narrowing the gutter takes
   out, or the cards climb toward the heading as a side effect of the card gap. */
#page-home .hp-capabilities__grid.grid-margin-y {
  margin-top: calc(4.25rem - var(--hp-gutter) / 2);
}

/* Split cards: copy above the figure on the left, figure above the copy on the
   right — the alternation in the Figma frame. */
#page-home .hp-split { display: flex; flex-direction: column; }
#page-home .hp-split--figure-last { justify-content: space-between; }
#page-home .hp-split--figure-first .hp-split__figure { order: -1; }

#page-home .hp-split__copy { padding: 2.5rem 2.5rem 0; }
#page-home .hp-split--figure-first .hp-split__copy { padding: 1.25rem 2.5rem 2.25rem; }

#page-home .hp-split__copy .hp-body { margin: 1rem 0 0; max-width: 42ch; }

/* The artwork runs to the card's own edges and is cropped by them — the frame
   shows the graph cut off at the left edge and the console prompt cut off at the
   right, and that crop is the composition. .hp-card clips it to the radius. */
#page-home .hp-split__figure { padding: 0; }

#page-home .hp-split__figure img {
  display: block;
  width: 100%;
  height: auto;
}

/* The bloom figure, once bloom-graph.js has drawn a frame over it. The export
   stays in the DOM rather than being replaced: it is the fallback for anyone
   the script never reaches, and hiding it with opacity instead of display keeps
   its alt text in the accessibility tree. */
#page-home .hp-bloom { position: relative; }

#page-home .hp-bloom__canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  pointer-events: none;
}

#page-home .hp-bloom img { transition: opacity 0.3s ease; }
#page-home .hp-bloom.is-live img { opacity: 0; }

/* The search card, once search-typing.js has inlined the export over it. Same
   fallback arrangement as the bloom: the <img> stays for anyone the script does
   not reach and keeps its alt text, hidden with opacity rather than display.
   The overlay is placed in px against the <img>'s own box by the script, which
   is what keeps it registered through the plate scaling below. */
#page-home .hp-typewriter { position: relative; }

#page-home .hp-typewriter__svg {
  position: absolute;
  height: auto;
  pointer-events: none;
}

#page-home .hp-typewriter img { transition: opacity 0.3s ease; }
#page-home .hp-typewriter.is-live img { opacity: 0; }

/* Solid while the line is being typed, blinking once it is finished — the way a
   real caret behaves. steps(1) so it snaps rather than fades. */
#page-home .hp-typewriter.is-typed .hp-typewriter__caret {
  animation: hp-caret-blink 1.06s steps(1) infinite;
}

@keyframes hp-caret-blink {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  #page-home .hp-typewriter__caret { animation: none; }
}

/* search-example.svg is a full console plate: its content stops at 319 of 666
   units and the rest is empty white, which as a whole image would leave a void
   half the card tall. Held to the frame's proportion instead and anchored to the
   top, so what shows is the prompt with the headroom the frame gives it. */
#page-home .hp-split--figure-first .hp-split__figure {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
}

/* The prompt runs off the card's right edge by design, but cut dead it reads as
   a mistake rather than a crop. The last third dissolves into the card instead,
   so the plate is deliberately trailing off. */
/* Both fades carry a z-index because search-typing.js appends its overlay as the
   last child of this figure. ::before is the first child in the box tree, so
   against a positioned sibling at z-index auto it would lose on DOM order and
   the right-hand fade would sit behind the artwork it is meant to soften. */
#page-home .hp-split--figure-first .hp-split__figure::before {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
  background: linear-gradient(to right, rgba(255, 255, 255, 0) 55%, #fff 95%);
}

/* The plate is a shade off white and carries a drop shadow, so cutting it dead
   leaves a visible seam across the card. Dissolve the last stretch into the card
   instead. */
#page-home .hp-split--figure-first .hp-split__figure::after {
  content: "";
  position: absolute;
  z-index: 1;
  right: 0;
  bottom: 0;
  left: 0;
  height: 72px;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 82%);
}

/* The plate inside the file is inset by the margin its drop shadow needs — 31 of
   626 across, 28 of 666 down. The frame has the plate flush with the card and no
   shadow at all, so the image is scaled and pulled up and left until the plate's
   own edges land on the card's and that margin is cropped away. Its depth is then
   whatever the figure's ratio leaves. */
#page-home .hp-split--figure-first .hp-split__figure img {
  /* max-width has to go with it: the theme's global img reset clamps every image
     to 100%, which would silently undo the scale and leave only the offset. */
  width: 110.993%;
  max-width: none;
  height: auto;
  margin: -4.965% 0 0 -5.496%;
}

@media screen and (max-width: 639px) {
  #page-home .hp-split__copy { padding: 1.5rem; }
  #page-home .hp-split--figure-first .hp-split__copy { padding: 1rem 1.5rem 1.5rem; }
}

/* Stat band */
#page-home .hp-statband { padding: 3rem 1rem; }

#page-home .hp-statband .cell { text-align: center; }

@media screen and (min-width: 640px) {
  #page-home .hp-statband .cell + .cell { border-left: 1px solid var(--hp-rule); }
}

@media screen and (max-width: 639px) {
  #page-home .hp-statband { padding: 2rem 1rem; }
  #page-home .hp-statband .cell + .cell {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid var(--hp-rule);
  }
}

/* Enterprise-grade card */
#page-home .hp-enterprise { min-height: 420px; }

#page-home .hp-enterprise__copy {
  position: relative;
  z-index: 1;
  padding: 3.5rem;
}

/* Only the prose clears the artwork. The figures sit below it and use the full
   card width, so "7.8 month" stays on one line instead of being clipped. */
#page-home .hp-enterprise__copy .hp-h3,
#page-home .hp-enterprise__copy .hp-body {
  max-width: 34ch;
}

#page-home .hp-enterprise__copy .hp-body {
  margin: 1rem 0 0;
  font-weight: 700;
  color: var(--hp-text);
}

#page-home .hp-enterprise__stats { margin-top: 4rem; }

/* A figure carrying a unit ("7.8 month") must not break between the number and
   the word. Its cell therefore has to be wide enough to hold the whole string:
   see the small-12 / medium-6 split in section-capabilities.php, which is what
   keeps this nowrap from turning into a clip. */
#page-home .hp-enterprise__stats .hp-figure { white-space: nowrap; }

/* The photo and its curved edge are separate assets: the mask carries the shape
   (smooth, low-frequency, upscales for free) and the photo comes from the
   design's embedded original at full resolution. Baking them together meant the
   art inherited the mask's resolution, which is what made it soft. */
#page-home .hp-enterprise__art {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 46%;
  background-image: url("mushroom-2.jpg");
  background-repeat: no-repeat;
  background-position: right top;
  /* Photo and mask are stretched identically to the box so they stay in
     register and the silhouette always lands on the card's edges. `cover` would
     crop the mask — and this card is shorter than the Figma one, so the crop
     ate the lower-left sweep of the curve. */
  background-size: 100% 100%;
  -webkit-mask-image: url("mushroom-mask.png");
  mask-image: url("mushroom-mask.png");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: right top;
  mask-position: right top;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  pointer-events: none;
}

@media screen and (max-width: 1023px) {
  #page-home .hp-enterprise { min-height: 0; }
  #page-home .hp-enterprise__copy { padding: 2rem; }
  #page-home .hp-enterprise__copy .hp-h3,
  #page-home .hp-enterprise__copy .hp-body { max-width: none; }
  #page-home .hp-enterprise__stats { margin-top: 2.5rem; }
  /* The shape mask carries its silhouette off its own bottom edge — which is
     right when the panel is the card's full height and the card's rounded corner
     finishes it, but as a 220px band the silhouette gets guillotined mid-card and
     the bottom right ends on a hard horizontal cut. A second mask layer fades the
     band's foot instead, so it dissolves into the card. */
  #page-home .hp-enterprise__art {
    position: relative;
    width: 100%;
    height: 220px;
    background-position: center 30%;
    -webkit-mask-image: url("mushroom-mask.png"), linear-gradient(to bottom, #000 55%, transparent 100%);
    mask-image: url("mushroom-mask.png"), linear-gradient(to bottom, #000 55%, transparent 100%);
    -webkit-mask-repeat: no-repeat, no-repeat;
    mask-repeat: no-repeat, no-repeat;
    -webkit-mask-position: right top, left top;
    mask-position: right top, left top;
    -webkit-mask-size: 100% 100%, 100% 100%;
    mask-size: 100% 100%, 100% 100%;
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   4. Partners — "We fit right in"
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-partners {
  position: relative;
  padding-top: 0;
  padding-bottom: 0;
  background: linear-gradient(180deg, #081E2B 0%, #010105 100%);
  overflow: hidden;
}

/* The stage is a 1440x837 coordinate space. Holding that aspect ratio is load
   bearing: the connector SVG stretches with preserveAspectRatio="none", so if
   the box drifts off-ratio the curves fall out of register with the copy. The
   stage is also allowed to run wider than .grid-container, because the Figma
   chips sit further out than the 1200px text column. */
#page-home .hp-constellation {
  position: relative;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
}

@media screen and (min-width: 1024px) {
  #page-home .hp-constellation { aspect-ratio: 1440 / 837; }
}

#page-home .hp-constellation__web {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

/* Copy sits at the hub. 46.8% puts the block where the frame has it, so the
   clear-zone ellipse the connectors terminate on lines up with the real text. */
#page-home .hp-constellation__copy {
  position: absolute;
  left: 50%;
  top: 46.8%;
  z-index: 2;
  width: min(34rem, 88%);
  transform: translate(-50%, -50%);
  text-align: center;
}

#page-home .hp-constellation__copy .hp-eyebrow {
  margin-bottom: 0.75rem;
  font-size: 12px;
  letter-spacing: 0.16em;
}

#page-home .hp-constellation__copy .hp-h2 {
  font-size: 70px;
  line-height: 1.04;
}

#page-home .hp-constellation__sub {
  margin: 1rem 0 0;
  font-size: 19px;
  font-weight: 700;
  color: #fff;
}

#page-home .hp-constellation__copy .hp-link {
  margin-top: 2.75rem;
  font-size: 17px;
}

#page-home .hp-chips {
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* The anchor owns placement and parallax; the chip owns its own entrance,
   drift and hover, so the two transforms never fight over one element. */
#page-home .hp-chip-anchor {
  position: absolute;
  transform: translate(-50%, -50%);
  translate: 0 calc(var(--depth, 0) * var(--par, 0) * -1px);
  will-change: translate;
}

#page-home .hp-chip {
  /* Ring thickness, shared by the border and the orbiting arc that traces it —
     they have to agree or the arc sits off the edge. A linked plate doubles it
     below; box-sizing is border-box, so that costs padding, not plate size. */
  --hp-chip-ring: 1px;

  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--chip-w, auto);
  flex: 0 0 auto;
  height: 52px;
  padding: 0 22px;
  background:
    linear-gradient(158deg, #1C2B37 0%, #15222C 48%, #101A22 100%);
  /* Kept, and transparent, rather than dropped: the plate is height- and
     width-constrained, so the border box has to stay the same shape whether the
     chip is outlined or not. Only a chip that links anywhere paints it. */
  border: var(--hp-chip-ring) solid transparent;
  border-radius: var(--hp-chip-radius, 14px);
  box-shadow:
    0 10px 26px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  transition: border-color 0.35s ease, box-shadow 0.35s ease, scale 0.35s ease;
}

/*
 * The outline is the affordance: a plate with a baltic edge goes somewhere, a
 * plate without one is a logo. It is on at rest, not on hover — a hover state
 * cannot tell a reader what is clickable before they point at it.
 *
 * Three things carry it, because one 1px edge among sixteen plates on a near
 * black field did not read: a 2px ring rather than 1, a plate lifted a few
 * steps out of the field so the difference is visible without pointing at
 * anything, and a soft baltic halo that separates it from the background. All
 * of it stays inside the design's palette — the lift is the same gradient
 * recipe, stepped up, not a different color.
 */
#page-home a.hp-chip {
  --hp-chip-ring: 2px;

  background:
    linear-gradient(158deg, #263D4C 0%, #1D3240 48%, #172833 100%);
  border-color: rgba(143, 227, 232, 0.5);
  box-shadow:
    0 10px 26px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 0 20px rgba(143, 227, 232, 0.1);
}

#page-home a.hp-chip:hover,
#page-home a.hp-chip:focus-visible {
  border-color: rgba(143, 227, 232, 0.95);
  box-shadow:
    0 14px 34px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    0 0 0 1px rgba(143, 227, 232, 0.22),
    0 0 26px rgba(143, 227, 232, 0.18);
  color: #fff;
  scale: 1.05;
  animation-play-state: paused;
}

/*
 * An arc that orbits the outline. A conic gradient rotated about the plate's
 * center and masked down to the 1px ring — the mask is the standard two-layer
 * exclude, which paints the border box and knocks the content box back out, so
 * nothing touches the plate itself.
 *
 * The angle has to be a registered property or it is not an animatable type and
 * the gradient simply jumps between keyframes. Where @property is unsupported
 * the arc never moves and the resting outline above is what shows, which is the
 * right thing to degrade to.
 *
 * Because the sweep is angular rather than along the perimeter, the arc runs
 * faster across the short ends of a wide plate than along its top and bottom.
 * Path-length animation would even that out at the cost of per-chip geometry;
 * at this size the difference does not read.
 */
@property --hp-orbit {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

#page-home a.hp-chip::after {
  content: "";
  position: absolute;
  /* The border box, not the padding box — an absolutely positioned child is
     laid out against the latter, which would leave the ring inside the edge it
     is meant to trace. Both values are the ring thickness, so the arc tracks
     whatever the border is set to. */
  inset: calc(var(--hp-chip-ring) * -1);
  /* The plate's radius plus the ring, not `inherit`: the arc sits a ring's width
     outside the plate, and inheriting the smaller radius clips its corners. */
  border-radius: calc(var(--hp-chip-radius, 14px) + var(--hp-chip-ring));
  padding: var(--hp-chip-ring);
  pointer-events: none;
  /* Reaches full brightness, and the lit segment is a little longer than the
     resting edge is bright — at 2px a faint arc just reads as a thicker line. */
  background: conic-gradient(
    from var(--hp-orbit),
    rgba(143, 227, 232, 0) 0deg,
    rgba(143, 227, 232, 0) 220deg,
    rgba(195, 248, 251, 1) 300deg,
    rgba(143, 227, 232, 0) 360deg
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
}

/* Held until the stage arrives, and desynchronized off the chip's own stagger
   so the five of them are not sweeping in lockstep. */
#page-home .hp-constellation.is-drawn a.hp-chip::after {
  animation: hp-chip-orbit 4.5s linear calc(var(--delay, 0s) * -4) infinite;
}

@keyframes hp-chip-orbit {
  to { --hp-orbit: 360deg; }
}

/* Sized by width, which is what the frame actually pins down — each mark's own
   aspect then sets its height. flex-shrink is off: the plate width is authored
   per chip, so the mark must keep its measured width rather than be squeezed by
   the plate's padding, which was shrinking the narrow marks by a third. */
#page-home .hp-chip img {
  flex: 0 0 auto;
  width: var(--logo-w, 100px);
  height: auto;
  max-height: 38px;
  object-fit: contain;
}

/* On the stage the plate width is explicit and the mark is centered, so padding
   would only eat into the mark. The stacked list keeps its padding. */
#page-home .hp-chips:not(.hp-chips--stacked) .hp-chip {
  padding: 0;
}

/* Remote brand assets arrive in their own colors; the frame renders them white.
   The marks lifted from the frame are already white with alpha, so they must not
   be filtered again. */
#page-home .hp-chip img:not(.hp-chip__mark) {
  filter: brightness(0) invert(1);
  opacity: 0.94;
}

#page-home .hp-chip span {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

#page-home .hp-chip--icon {
  --hp-chip-radius: 15px;

  width: 52px;
  padding: 0;
}

/* ── constellation motion ────────────────────────────────────────────────── */

/* No stroke color here on purpose: each run carries its own gradient, written
   by the template along that run's own chord so it fades out at the end left
   hanging in open space. The color lives in the gradient stops — gray for a
   plain run, baltic for one that ends at a chip you can follow — which is also
   what keeps the two from being set in two places. */
#page-home .hp-edge {
  stroke-width: 1;
}

#page-home .hp-pulse { fill: #8FE3E8; }

/* Edges fade up whole, chips arrive on the same beat, then a pulse runs each
   edge continuously. Staggered by --delay, so the web resolves as a cascade
   rather than all at once. */
#page-home .hp-constellation.is-armed .hp-edge { opacity: 0; }

#page-home .hp-constellation.is-drawn .hp-edge {
  animation: hp-edge-in 1.5s ease-out var(--delay, 0s) forwards;
}

@keyframes hp-edge-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

#page-home .hp-pulse { opacity: 0; }

#page-home .hp-constellation:not(.is-armed) .hp-pulse { opacity: 0.85; }

/* The only mark on an edge now, so it carries the full glow. */
#page-home .hp-constellation.is-drawn .hp-pulse {
  opacity: 1;
  transition: opacity 0.8s ease 1.2s;
  filter: drop-shadow(0 0 7px rgba(143, 227, 232, 0.9));
}

#page-home .hp-constellation.is-armed .hp-chip { opacity: 0; }

#page-home .hp-constellation.is-drawn .hp-chip {
  animation:
    hp-chip-in 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) var(--delay, 0s) forwards,
    hp-chip-drift var(--drift, 7s) ease-in-out calc(var(--delay, 0s) + 1s) infinite;
}

@keyframes hp-chip-in {
  from { opacity: 0; transform: translateY(14px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes hp-chip-drift {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* ── small screens: a plain wrapped row, no stage ─────────────────────────── */

#page-home .hp-chips--stacked {
  position: static;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
  margin-top: 3rem;
}

#page-home .hp-chips--stacked .hp-chip-anchor {
  position: static;
  transform: none;
  translate: none;
  left: auto;
  top: auto;
}

/* Every chip takes its whole cell, so the block reads as an even grid rather
   than a ragged wrap — icon-only chips would otherwise sit as 52px squares
   between ~170px wordmarks. */
#page-home .hp-chips--stacked .hp-chip,
#page-home .hp-chips--stacked .hp-chip--icon {
  width: 100%;
  height: 56px;
  padding: 0 1rem;
}

/* Marks keep their authored width but may shrink to fit a narrow cell. */
#page-home .hp-chips--stacked .hp-chip img {
  flex: 0 1 auto;
  max-width: 100%;
}

@media screen and (max-width: 1023px) {
  #page-home .hp-constellation__web,
  #page-home .hp-chips:not(.hp-chips--stacked) { display: none; }

  /* The stage is a direct child of the section (no .grid-container, because the
     desktop chips must be able to sit outside the 1200px text column), so it has
     to supply its own gutter here or the grid runs to the viewport edge. */
  #page-home .hp-constellation {
    padding-left: 1.5rem;
    padding-right: 1.5rem;
  }

  /* No entrance or drift on the stacked grid — the scatter animation belongs to
     the constellation, and on a plain two-column list it just makes the pills
     twitch. They are simply present. */
  #page-home .hp-constellation.is-armed .hp-chip,
  #page-home .hp-constellation.is-drawn .hp-chip {
    opacity: 1;
    animation: none;
  }

  #page-home .hp-chips--stacked .hp-chip-anchor { translate: none; }

  #page-home .hp-constellation__copy {
    position: static;
    left: auto;
    top: auto;
    width: auto;
    transform: none;
  }

  #page-home .hp-constellation__copy .hp-h2 { font-size: 40px; }
}

@media screen and (min-width: 1024px) {
  #page-home .hp-chips--stacked { display: none; }
}

/* Declared after the base `padding: 0` above, which only holds on desktop where
   the stage is the section. Small screens need real breathing room below the
   chip grid. */
@media screen and (max-width: 1023px) {
  #page-home .hp-partners {
    padding-top: 3.5rem;
    padding-bottom: 4.5rem;
  }
}

@media screen and (max-width: 639px) {
  #page-home .hp-constellation__copy .hp-h2 { font-size: 32px; }
  #page-home .hp-chip { font-size: 15px; }
  #page-home .hp-chips--stacked .hp-chip,
  #page-home .hp-chips--stacked .hp-chip--icon { height: 52px; padding: 0 0.75rem; }
  #page-home .hp-chip img { max-height: 30px; }
  #page-home .hp-chip span { font-size: 17px; }
}

@media (prefers-reduced-motion: reduce) {
  #page-home .hp-edge,
  #page-home .hp-chip,
  #page-home a.hp-chip::after {
    opacity: 1;
    animation: none !important;
  }
  #page-home .hp-chip-anchor { translate: none !important; }
  #page-home .hp-pulse { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   5. Customers — "Loved by devs. Deployed worldwide."
   ═══════════════════════════════════════════════════════════════════════════ */

/* Frame: the band is 1144 tall with the card ending 141px above the bottom edge,
   so the padding is asymmetric — more below the card than above the heading. */
#page-home .hp-customers {
  padding-top: 5rem;
  padding-bottom: 8.75rem;
  background: #fff;
}

#page-home .hp-customers .hp-h2 { text-align: center; color: var(--hp-text); }

#page-home .hp-tablist {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 6.5rem 0 1.8rem;
  /* Shadows must be free to paint outside the row — the frame lets the active
     tab's shadow spill into the gutter exactly as the card's does. Clipping here
     cut the first and last tabs off. */
  overflow: visible;
}

#page-home .hp-tab {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 8.5rem;
  height: 88px;
  /* The inset is what gives each mark its breathing room inside the plate — the
     wide wordmarks are held by it rather than by their own height. */
  padding: 0 2rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}

#page-home .hp-tab img {
  flex: 0 0 auto;
  max-width: 100%;
  max-height: 2rem;
  width: auto;
  object-fit: contain;
  filter: grayscale(1) brightness(0);
  opacity: 0.47;
  transition: opacity 0.3s ease;
}

#page-home .hp-tab:hover img { opacity: 0.68; }

/* Frame check: the active tab's edge is a pure soft falloff to ~7% with a clean
   jump to white — no border line at all — and the profile matches the card's. */
#page-home .hp-tab[aria-selected="true"] {
  background: #fff;
  box-shadow: var(--hp-card-shadow);
}

#page-home .hp-tab[aria-selected="true"] img {
  opacity: 0.94;
}

#page-home .hp-tab:focus-visible {
  outline: 2px solid var(--hp-baltic-50);
  outline-offset: 2px;
}

/* Only the open panel is in flow; the rest are stacked on top of it and taken
   out of the height. They used to share one grid cell, which sized the stack to
   the TALLEST panel — and the three summary panels run ~400px taller than the
   quote ones once their figures stack, so a quote card was padded out with that
   much empty white below its copy. */
#page-home .hp-stories {
  position: relative;
}

#page-home .hp-stories > .hp-story {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity 0.45s ease, transform 0.55s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0.45s;
}

#page-home .hp-stories > .hp-story.is-active {
  position: relative;
  opacity: 1;
  visibility: visible;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  #page-home .hp-stories > .hp-story { transition: none; }
}

#page-home .hp-story {
  position: relative;
  background: #fff;
  border-radius: 16px;
  box-shadow: var(--hp-card-shadow);
  overflow: hidden;
}



/* The card's height, set from the inside: the copy is the only child in the flow
   (the art is absolute), so its min-height is what the card measures. 589 is the
   card in the frame — 1152x589, from the card export's own rounded rect — and no
   panel's content comes within 60px of it, so every card lands on that number
   rather than on whichever story happens to be longest. */
#page-home .hp-story__copy {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  min-height: 589px;
  padding: 5rem;
}

/* Only the prose clears the artwork; the figures below it span the whole card,
   which is what stops their labels wrapping to three lines. */
#page-home .hp-story__text,
#page-home .hp-story__attribution,
#page-home .hp-story__copy .hp-link {
  max-width: 46rem;
}

/* .hp-story__copy is a flex column (so the figures can pin to the bottom), and
   a column flex container stretches its children across the cross axis. With
   object-fit's default of `fill` that stretched these logos to the full copy
   width — 686x40 for a 337x119 mark. align-self stops the stretch; object-fit
   is the belt-and-braces guard, and the height comes per story from the frame,
   where the marks are optically sized rather than uniform. */
/* Frame: every story-card logo is fully desaturated and dark — measured
   max_sat 0.000 on all six, with darkest values of 0-39. The dist assets arrive
   in brand color (Merck magenta, Intuit blue), which is why Merck was reading
   bright pink on a white card.
   grayscale alone leaves the colored marks as washed-out mid-grays, so a modest
   brightness cut pushes them toward the frame's weight; logos that are already
   near-black are unaffected by it. A cut rather than brightness(0), so a mark
   carrying internal tone keeps it instead of collapsing to a silhouette. */
#page-home .hp-story__logo {
  filter: grayscale(1) brightness(0.55);
  display: block;
  align-self: flex-start;
  flex: 0 0 auto;
  width: auto;
  height: var(--logo-h, 32px);
  max-width: 60%;
  object-fit: contain;
  object-position: left center;
  margin-bottom: 1.75rem;
}

#page-home .hp-story__text {
  margin: 0;
  font-size: 24px;
  line-height: 1.4;
  color: var(--hp-text);
}

/* A <blockquote>, so it arrives with the theme's rule and indent. The frame has
   neither — the quote sits flush under the logo, like the summary copy does. */
#page-home .hp-story__text--quote {
  padding: 0;
  border-left: 0;
  font-size: 28px;
  line-height: 1.4;
}

#page-home .hp-story__attribution { margin: 1.5rem 0 0; }

#page-home .hp-story__name {
  display: block;
  font-size: 15px;
  font-weight: 700;
  color: var(--hp-text);
}

#page-home .hp-story__role {
  display: block;
  font-size: 15px;
  color: #90979F;
}

#page-home .hp-story__copy .hp-link { margin-top: 2rem; }

#page-home .hp-story__stats { margin-top: auto; }

/* The row carries an x gutter only, so once the cells stack there is nothing
   between them and a label runs straight into the next figure. */
@media screen and (max-width: 767px) {
  #page-home .hp-story__stats .cell + .cell { margin-top: 1.75rem; }
}

/* Exported on white, same treatment as the enterprise art.
   contain, not cover: the exports are 348x404 (0.861) and this box is nearer
   0.887, so cover scaled them to the width and took about 12px off the bottom
   of every photo. contain fits the whole image whatever the column works out
   to, and the few px it leaves over land on the card's own white — the same
   white the art is exported on — so the fit costs nothing to look at. */
/* The art's frame, from the card export: the card is 1152 wide (which is what
   this template renders at full size) and the petal is 348x404 against its
   top-right corner — so 30% by 404, and `contain` then lands the export at its
   own 1:1 size. At 390 it was scaled to 96% and sat 14px short of where the
   curve is meant to close. */
#page-home .hp-story__art {
  position: absolute;
  top: 0;
  right: 0;
  width: 30%;
  height: 404px;
  background-repeat: no-repeat;
  background-position: right top;
  background-size: contain;
  pointer-events: none;
}

@media screen and (max-width: 1023px) {
  #page-home .hp-story { min-height: 0; }
  #page-home .hp-story__copy { min-height: 0; padding: 2rem; }
  #page-home .hp-story__stats { margin-top: 2.5rem; }
  #page-home .hp-story__text,
  #page-home .hp-story__attribution,
  #page-home .hp-story__copy .hp-link { max-width: none; }
  /* The art is exported with its curved mask cut out of the left side. As a
     full-width banner that notch reads as a stray white wedge, so anchor right
     and keep the photographic part in frame. */
  #page-home .hp-story__art {
    position: relative;
    width: 100%;
    height: 200px;
    background-position: right top;
    /* Stays cover here: as a full-width 200px banner the box is far wider than
       the export, and containing it would shrink the photo to a stamp against
       the right edge instead of filling the band. */
    background-size: cover;
  }
  #page-home .hp-story__text--quote { font-size: 21px; }
  #page-home .hp-tablist {
    margin: 2rem 0 1.5rem;
    justify-content: flex-start;
    overflow-x: auto;
    scrollbar-width: none;
    /* room for the active tab's shadow inside the scroll container */
    padding: 0.75rem 0;
  }
  #page-home .hp-tablist::-webkit-scrollbar { display: none; }
  /* In the scroller the plate is content-sized, so the marks sit at their own
     size and only the plate grows around them — which left them larger here than
     in the desktop row. A fixed plate gives max-width something to bind to, the
     way flex: 1 does above. */
  #page-home .hp-tab {
    flex: 0 0 auto;
    width: 9.5rem;
    padding: 0 1.5rem;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   6. GraphRAG — "Manage context and build agentic GraphRAG"
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-graphrag {
  padding-top: 7.5rem;
  padding-bottom: 7.5rem;
  background: #081E2B;
}

#page-home .hp-graphrag .hp-body { margin: 1.5rem 0 0; max-width: 46ch; }
#page-home .hp-graphrag .hp-link { margin-top: 2rem; }

#page-home .hp-graphrag__figure {
  display: block;
  /* Smoother curve tessellation on a path this complex. */
  shape-rendering: geometricPrecision;
  width: 100%;
  height: auto;
  max-width: 660px;
  margin-left: auto;
}

#page-home .hp-graphrag__label {
  font-size: 14px;
  font-weight: 400;
  fill: #fff;
}

/* The labels arrive after the outline has closed around them. blob-draw.js arms
   them; if it never runs they are simply there, like the rest of the figure. */
#page-home .hp-graphrag__label text.is-armed { opacity: 0; }

#page-home .hp-graphrag__label text.is-arriving {
  animation: hp-blob-label 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes hp-blob-label {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  #page-home .hp-graphrag__label text.is-armed,
  #page-home .hp-graphrag__label text.is-arriving {
    opacity: 1;
    animation: none;
  }
}

/* The spotlight layer is a third copy of the union, painted over the fill and
   under the labels so the type stays legible. It only appears on pointer hover;
   its center is moved in user units by the section's script. */
/* Now a single path (the Figma export is one closed shape), painted over the fill
   and under the labels so the type stays legible. */
#page-home .hp-blob-spot {
  opacity: 0;
  transition: opacity 0.45s ease;
  pointer-events: none;
}

#page-home .hp-graphrag__figure.is-lit .hp-blob-spot {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  #page-home .hp-blob-spot { display: none; }
}

@media screen and (max-width: 1023px) {
  #page-home .hp-graphrag__figure { margin: 2.5rem auto 0; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   7. What's new
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-news {
  padding-top: 5.5rem;
  padding-bottom: 5.5rem;
  background: linear-gradient(233deg, #014063 17.57%, #081E2B 63.41%);
}

#page-home .hp-news .hp-h2 { margin-bottom: 2.5rem; }

#page-home .hp-news__grid { --hp-gutter: 16px; }

#page-home .hp-news-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #fff;
  border-radius: 6px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.22);
  overflow: hidden;
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

#page-home .hp-news-card:hover { transform: translateY(-8px); }

/* A 40px window onto a much taller photo. The image drifts through it on a very
   slow alternating loop — no jump, since it reverses rather than resetting — and
   pauses while the card is hovered, so the motion never fights with reading.
   translateY is used rather than object-position so the pan stays on the
   compositor. */
#page-home .hp-news-card__banner {
  position: relative;
  display: block;
  width: 100%;
  height: 40px;
  overflow: hidden;
}

#page-home .hp-news-card__img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 210px;
  object-fit: cover;
  transform: translateY(-56px);
  transition: scale 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
}

@media (prefers-reduced-motion: no-preference) {
  #page-home .hp-news-card__img {
    animation: hp-news-pan 34s ease-in-out var(--pan-delay, 0s) infinite alternate;
  }

  #page-home .hp-news-card:hover .hp-news-card__img {
    animation-play-state: paused;
  }
}

@keyframes hp-news-pan {
  from { transform: translateY(-6px); }
  to   { transform: translateY(-164px); }
}

/* Hovering also eases the photo in a touch, so the pause reads as intentional. */
#page-home .hp-news-card:hover .hp-news-card__img { scale: 1.05; }

#page-home .hp-news-card__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: 1.5rem;
}

#page-home .hp-news-card__title {
  margin: 0 0 0.75rem;
  font-size: 24px;
  line-height: 1.3;
  font-weight: 700;
  color: var(--hp-text);
}

#page-home .hp-news-card__body .hp-body { margin: 0; }

/* Link parks at the bottom so the three cards line up however long the copy. */
#page-home .hp-news-card__body .hp-link { margin-top: auto; padding-top: 2.5rem; }

/* Stacked, there is no row to line up with and that reserve is only a gap. */
@media screen and (max-width: 639px) {
  #page-home .hp-news-card__body .hp-link { padding-top: 1.5rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   8. FAQ — "Get to know Neo4j"
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-faq {
  padding-top: 6.25rem;
  padding-bottom: 6.25rem;
  background: #fff;
}

#page-home .hp-faq .hp-h2 { text-align: center; color: var(--hp-text); }

#page-home .hp-faq__list {
  max-width: 60rem;
  margin: 3.25rem auto 0;
  border: 1px solid var(--hp-rule-strong);
  border-radius: var(--hp-card-radius);
  overflow: hidden;
}

#page-home .hp-faq__item + .hp-faq__item { border-top: 1px solid var(--hp-rule-strong); }

#page-home .hp-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.8rem 2.5rem;
  font-size: 20px;
  font-weight: 700;
  color: var(--hp-text);
  cursor: pointer;
  list-style: none;
}

#page-home .hp-faq__q::-webkit-details-marker { display: none; }

#page-home .hp-faq__q:hover .hp-faq__chevron { color: var(--hp-baltic-50); }

#page-home .hp-faq__q:focus-visible {
  outline: 2px solid var(--hp-baltic-50);
  outline-offset: -2px;
}

#page-home .hp-faq__chevron {
  flex: 0 0 auto;
  color: var(--hp-text);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), color 0.25s ease;
}

#page-home .hp-faq__item[open] .hp-faq__chevron { transform: rotate(180deg); }
#page-home .hp-faq__item[data-closing] .hp-faq__chevron { transform: rotate(0deg); }

/* The panel is what the script animates (height 0 <-> content). overflow must be
   hidden or the answer spills during the collapse. */
#page-home .hp-faq__panel {
  overflow: hidden;
}

/* The answer eases up into place slightly behind the height, so the row opens as
   one gesture rather than the text simply appearing at full size. */
#page-home .hp-faq__item[open] .hp-faq__a {
  animation: hp-faq-answer 0.42s cubic-bezier(0.22, 1, 0.36, 1) 0.06s both;
}

/* While closing, the same easing runs in reverse — without this the text would
   sit at full opacity right up to the moment the row snaps shut. */
#page-home .hp-faq__item[data-closing] .hp-faq__a {
  animation: hp-faq-answer 0.24s cubic-bezier(0.4, 0, 0.6, 1) both reverse;
}

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

@media (prefers-reduced-motion: reduce) {
  #page-home .hp-faq__item[open] .hp-faq__a,
  #page-home .hp-faq__item[data-closing] .hp-faq__a { animation: none; }
}

#page-home .hp-faq__a {
  padding: 0 2.5rem 1.8rem;
  margin: -0.35rem 0 0;
  font-size: 16px;
  line-height: 1.5;
  color: var(--hp-text);
  max-width: 80ch;
}

/* These links sit mid-sentence in 16px body copy, where color on its own is
   easy to read straight past. Scoped to the FAQ rather than put on
   .hp-inline-link: the statement band uses the same class on display type that
   is already the largest thing on the screen. Public Sans carries a real 700,
   so this is a face and not a synthesized bold. */
#page-home .hp-faq .hp-inline-link {
  font-weight: 700;
}

@media screen and (max-width: 639px) {
  #page-home .hp-faq__q { padding: 1.25rem 1.25rem; font-size: 16px; }
  #page-home .hp-faq__a { padding: 0 1.25rem 1.25rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   9. Closing CTA — "Knowledge is at your fingertips"
   ═══════════════════════════════════════════════════════════════════════════ */

#page-home .hp-finalcta {
  position: relative;
  background: #012132;
  text-align: center;
  overflow: hidden;
}

/* The wavy sweep on the right of the Figma frame — traced export. */
#page-home .hp-finalcta__shape {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: auto;
  aspect-ratio: 570 / 388;
  pointer-events: none;
}

#page-home .hp-finalcta > .grid-container { position: relative; z-index: 1; }

#page-home .hp-finalcta .hp-h2 { font-size: 48px; }

#page-home .hp-finalcta__sub {
  margin: 1rem 0 0;
  font-size: 17px;
  font-weight: 700;
  color: #fff;
}

#page-home .hp-finalcta__actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2.5rem;
  flex-wrap: wrap;
  margin-top: 2.5rem;
}

#page-home .hp-finalcta__actions .button { margin-bottom: 0; }

#page-home .hp-finalcta__link {
  font-size: 17px;
  font-weight: 700;
  color: var(--hp-baltic-15);
  text-decoration: none;
}

#page-home .hp-finalcta__link:hover,
#page-home .hp-finalcta__link:focus {
  color: var(--hp-baltic-20);
  text-decoration: none;
}

/* Below 1023px the headline wraps to 3 lines, inflating section height —
   size the shape off the viewport width instead or it blows up off-screen. */
@media screen and (max-width: 1023px) {
  #page-home .hp-finalcta__shape {
    top: 0;
    height: auto;
    width: 65vw;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   10. The header over the hero

   The site header is shared markup, outside #page-home — these are the only
   rules in this file that reach past the page wrapper. They are scoped three
   ways: the stylesheet loads on this template alone, every selector sits under
   body.hp-home — a class the template adds, not WordPress's own .home, so the
   treatment travels with this design rather than with whichever page is the
   front page — and none of it applies until the inline primer in
   page-home.php has put .hp-nav-live on <html>. Without JS the
   site's own header is untouched and the hero stays below it.

   Two things can make the bar solid, and they want opposite treatment.

   --hp-nav is the scroll blend, written per frame by the primer: 0 is clear
   over the hero, 1 is the header the rest of the site has. It must not be
   transitioned — the scroll is the timeline, and easing it would only make the
   bar lag the page.

   --hp-menu is an open mega menu, which is a state change rather than a
   position, and snapping the whole bar from clear to white in one frame is the
   jarring part. That one is eased.

   --hp-hover is a pointer anywhere on the bar or the banner. Glass is a nice
   idle state and a poor one to aim at: over a bright patch of the hero the
   labels are the only thing holding the row together, and a reader who has
   moved the pointer up there is reading, not looking at the picture. It eases
   like --hp-menu, in both directions, because nothing else is moving with it.

   --hp-solid is whichever of them is furthest along, and it is the value every
   property below actually reads. The fallback in --hp-nav is 1, so anything
   that reads it before the primer has written it gets the ordinary header
   rather than an invisible one.

   --hp-menu has to be a registered property or it is not an animatable type
   and there is nothing to transition; it inherits so the bar's eased value is
   what the logo and the icons see too. Where @property is unsupported it stays
   at 0 and the menu-open state simply switches, which is where this started. */

@property --hp-menu {
  syntax: "<number>";
  initial-value: 0;
  inherits: true;
}

@property --hp-hover {
  syntax: "<number>";
  initial-value: 0;
  inherits: true;
}

/*
   ═══════════════════════════════════════════════════════════════════════════ */

html.hp-nav-live body.hp-home {
  --hp-navh: 70px;

  /* On body rather than on the bar: the announcement banner is a sibling above
     the header, not a descendant of it, and it takes the same blend. */
  --hp-solid: max(var(--hp-nav, 1), var(--hp-menu), var(--hp-hover));

  /* Declared here, not on the hover state, so the fade runs on the way out too.
     --hp-menu does the opposite on purpose; see that rule. */
  transition: --hp-hover 0.28s cubic-bezier(0.33, 0, 0.2, 1);
}

/*
 * The bar and the banner solidify together from a pointer over either, because
 * they are one surface to look at even though they are siblings in the markup:
 * lighting only the half under the pointer would draw a seam across the top of
 * the page that is not there at any other time.
 *
 * :has() on the body for the same reason --hp-solid lives there. Gated on a
 * real pointer — on a touchscreen :hover latches after a tap and the bar would
 * simply stay white until something else was touched.
 */
@media (hover: hover) {
  html.hp-nav-live body.hp-home:has(#neo4j-main-menu-container:hover, .n-mobile-header-bar:hover, #callout-banner:hover) {
    --hp-hover: 1;
  }
}

@media screen and (min-width: 1024px) {
  html.hp-nav-live body.hp-home { --hp-navh: 76px; }
}

/* The hero comes up to meet the bar. Without this the bar is simply above the
   hero in the flow and there is nothing behind it to see through to. The copy
   does not move: the section's top padding grows by exactly what the negative
   margin takes away. */
html.hp-nav-live body.hp-home #page-home .hp-hero {
  margin-top: calc(var(--hp-navh) * -1);
  padding-top: calc(6rem + var(--hp-navh) - var(--hp-hero-squeeze) * 0.4);
}

/*
 * With a banner running, the hero reaches up behind that too — there is no point
 * making a bar transparent if what is behind it is the page's own white. The
 * copy does not move: the top padding grows by exactly what the margin takes
 * away, banner included. --hp-annh is measured and published by the primer,
 * because the banner's height is whatever its text wraps to.
 */
html.hp-ann-live body.hp-home #page-home .hp-hero {
  margin-top: calc((var(--hp-navh) + var(--hp-annh, 0px)) * -1);
  padding-top: calc(6rem + var(--hp-navh) + var(--hp-annh, 0px) - var(--hp-hero-squeeze) * 0.4);
}

/*
 * The banner's markup is Foundation's old float grid — one full-width .columns
 * with float: left. That float has to go, and it is load-bearing that it does:
 * the hero is a block formatting context (overflow: hidden holds the parallax
 * backdrop in), and a BFC narrows itself to avoid a float it sits beside. Pulled
 * up alongside this one, the hero collapsed from full width to the width of its
 * longest word. A single column spanning the row does not need to float, so
 * dropping it changes nothing about how the banner reads.
 */
html.hp-ann-live body.hp-home #callout-banner .columns {
  float: none;
}

/* The hero is later in the document than the header, so without a layer of its
   own the banner would be painted over by it rather than sitting on top.

   40 stays under everything else in the header — the off-canvas menu in
   particular, which is fixed at 50 and opens over the whole screen: a banner
   above that covers its close button and traps the visitor in the menu. */
html.hp-ann-live body.hp-home #callout-banner {
  position: relative;
  z-index: 40;
}

html.hp-nav-live body.hp-home #neo4j-main-menu-container.n-header-bar,
html.hp-nav-live body.hp-home .n-mobile-header-bar {
  --hp-nav-ink: color-mix(in srgb, #fff calc((1 - var(--hp-solid)) * 100%), #1A1B1D);


  /* Nothing at all over the hero: the only fill is the site's own white, ramping
     in as the bar comes back. The frame tints the bar #04182366, but that reads
     as a blue band sitting on the object rather than as glass. */
  background-color: rgba(255, 255, 255, var(--hp-solid));
  /* Two layers, and at either end of the blend one of them is at zero alpha: the
     frame's drop shadow while the bar is glass, the site's own once it is back.

     Both are the nav's own shadow ink. The frame names its color
     var(--drop-shadowsm), which does not exist here — the design system's token
     pipeline carries palette colors, not shadows — so the value comes from the
     layer that does own it: _nav-tokens.scss, where $n-nav-bar-shadow is
     rgba(21, 30, 41, 0.08) and $n-nav-mega-shadow is the same ink at 0.12. The
     geometry is the frame's. That leaves one ink across both layers rather than
     a black that matches nothing else in the header. */
  box-shadow:
    0 2px 8px 2px rgba(21, 30, 41, calc((1 - var(--hp-solid)) * 0.16)),
    0 1px 3px rgba(21, 30, 41, calc(var(--hp-solid) * 0.08));
}

/* Everything in the bar inherits one ink, so one mix covers the labels, the
   utility links and the search control. */
html.hp-nav-live body.hp-home #neo4j-main-menu-container,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__trigger,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__label,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-utility-nav__link,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-utility-nav__search {
  color: var(--hp-nav-ink);
}

/* The chevrons are a mask filled by background-color, not a glyph — color
   alone does not reach them. */
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__chevron {
  background-color: var(--hp-nav-ink);
}

/* The search mark and the mobile menu mark are data-URI artwork with the color
   baked in. Inverting is the only way to carry them across, and it lands on the
   same near-white the mix does. */
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-icon-search,
html.hp-nav-live body.hp-home .n-mobile-header-bar .n-icon {
  /* drop-shadow after the invert, so it is the white mark that casts it. */
  filter:
    invert(calc(1 - var(--hp-solid)))
    drop-shadow(0 1px 2px rgba(3, 12, 20, calc((1 - var(--hp-solid)) * 0.5)));
}

/*
 * A shadow under the header's own text while the bar is clear.
 *
 * The bars carry no fill over the hero, and between roughly 100px and 500px of
 * scroll the densest part of the object passes beneath them — white type on
 * white bokeh. The two utility links on the right and the banner's centered line
 * sit right over where the object is brightest. This holds them without putting
 * a color back on the bar, which is the thing design did not want.
 *
 * Two layers: a tight one that gives the glyph edges something to sit against,
 * and a wide soft one that darkens the field behind a whole word. Both fade out
 * with the fill, so by the time the bar is white there is no shadow left under
 * what is now dark text on white.
 */
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__trigger,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__label,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-utility-nav__link,
html.hp-ann-live body.hp-home #callout-banner .columns > p,
html.hp-ann-live body.hp-home #callout-banner .columns > p a {
  text-shadow:
    0 1px 2px rgba(3, 12, 20, calc((1 - var(--hp-solid)) * 0.5)),
    0 0 14px rgba(3, 12, 20, calc((1 - var(--hp-solid)) * 0.38));
}

/* The chevrons are a mask, not a glyph, so text-shadow cannot reach them and
   they would be the one thing on the row left unbacked. */
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__chevron {
  filter: drop-shadow(0 1px 2px rgba(3, 12, 20, calc((1 - var(--hp-solid)) * 0.5)));
}

/* Lighter over the hero, the site's own weight once the bar is back. Public
   Sans is loaded as static faces (300/400/600/700), so this is a swap rather
   than a blend — it lands in the middle of the color change, where it is least
   visible. */
html.hp-nav-clear body.hp-home #neo4j-main-menu-container .n-main-nav__trigger,
html.hp-nav-clear body.hp-home #neo4j-main-menu-container .n-main-nav__label,
html.hp-nav-clear body.hp-home #neo4j-main-menu-container .n-utility-nav__link {
  font-weight: 300;
}

/* The hover affordance crosses the blend like everything else on the bar. Over
   the hero it is a white wash, because the bar's own teal tint is invisible on
   glass; by the time the bar is white it IS that tint, so a hovered item here
   looks like a hovered item on any other page.

   Written out here because the ink and fill rules above outrank the shared
   header's own hover, which would otherwise do this itself. The values are that
   header's, from _nav-tokens.scss: $n-nav-hover-bg is rgba(48, 131, 157, 0.08)
   and $n-nav-text-primary-initial is #0A6190.

   Redefining --hp-nav-ink rather than setting color is what carries the chevron
   along — it is a mask painted with that same variable, and it inherits. */
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__trigger:hover,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-main-nav__trigger:focus-visible,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-utility-nav__link:hover,
html.hp-nav-live body.hp-home #neo4j-main-menu-container .n-utility-nav__link:focus-visible {
  --hp-nav-ink: color-mix(in srgb, #0A6190 calc(var(--hp-solid) * 100%), #fff);

  background-color: color-mix(
    in srgb,
    rgb(48 131 157 / 8%) calc(var(--hp-solid) * 100%),
    rgb(255 255 255 / 12%)
  );
}

/* The logo is the header's own asset twice over: the original, and a copy of
   the same file knocked flat white. The URL comes from the markup by way of
   --hp-logo, set by the primer, so the header partial stays the only place it
   is written down — and because both layers are the same artwork they are in
   exact register through the cross-fade. */
html.hp-nav-live body.hp-home .n-header-bar__logo,
html.hp-nav-live body.hp-home .n-mobile-header-bar > a {
  position: relative;
}

html.hp-nav-live body.hp-home .n-header-bar__logo img,
html.hp-nav-live body.hp-home .n-mobile-header-bar > a img {
  opacity: var(--hp-solid);
}

html.hp-nav-live body.hp-home .n-header-bar__logo::after,
html.hp-nav-live body.hp-home .n-mobile-header-bar > a::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--hp-logo) left center / contain no-repeat;
  filter: brightness(0) invert(1);
  opacity: calc(1 - var(--hp-solid));
}

/*
 * An open mega menu is a white panel; the bar above it has to be white too, or
 * the panel hangs off the end of nothing. The open state lives on the nav item
 * (is-mega-open, set by the header's own script), so the bar reads it downward.
 *
 * The bar and the panel have to arrive TOGETHER. The panel has no transition of
 * its own — the shared header shows it by flipping display — so easing only the
 * bar put a fully white panel under a fully transparent strip for the length of
 * the ease, with a hard seam between them. Two surfaces landing at different
 * times reads far worse than either one snapping.
 *
 * So the panel is given the same fade below, and the transition here sits on the
 * open state rather than the base rule: a transition is taken from the style the
 * element is moving TO, so declaring it here eases the way in and leaves the way
 * out instant — which is what keeps the two in step on the way out too, since
 * the panel's own exit is a discrete display flip.
 */
html.hp-nav-live body.hp-home:has(#neo4j-main-menu-container .n-nav-item.is-mega-open) {
  --hp-menu: 1;

  /* --hp-hover is carried along because this shorthand replaces the one on the
     base rule, and dropping it would strand a half-faded hover mid-transition. */
  transition:
    --hp-menu 0.28s cubic-bezier(0.33, 0, 0.2, 1),
    --hp-hover 0.28s cubic-bezier(0.33, 0, 0.2, 1);
}

/*
 * The panel, on the same curve. @starting-style is what makes a fade out of
 * display:none possible at all — without a style to start from there is nothing
 * to interpolate and the panel simply appears, which is the behavior this
 * replaces and the behavior a browser without it keeps.
 *
 * Entry only. Giving the exit a fade as well would need display to transition
 * discretely, and then moving from one menu to the next would cross-fade two
 * panels over each other in the same box.
 */
html.hp-nav-live body.hp-home #neo4j-main-menu-items > .n-nav-item > .n-mega-menu {
  transition: opacity 0.28s cubic-bezier(0.33, 0, 0.2, 1);
}

@starting-style {
  html.hp-nav-live body.hp-home #neo4j-main-menu-items > .n-nav-item > .n-mega-menu.is-active {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  html.hp-nav-live body.hp-home:has(#neo4j-main-menu-container .n-nav-item.is-mega-open),
  html.hp-nav-live body.hp-home #neo4j-main-menu-items > .n-nav-item > .n-mega-menu {
    transition: none;
  }
}
