/* =========================================================
   Looptimize 2026 — cosmic redesign stylesheet
   ========================================================= */

:root {
  /* Surfaces — deep space */
  --bg: oklch(0.13 0.06 285);
  --bg-elev: oklch(0.18 0.07 290);
  --bg-card: oklch(0.21 0.08 290 / 0.6);
  --fg: oklch(0.97 0.01 300);
  --fg-muted: oklch(0.95 0.015 305);
  --fg-dim: oklch(0.88 0.02 305);
  --border: oklch(1 0 0 / 0.08);
  --border-strong: oklch(1 0 0 / 0.16);

  /* Cosmic accents */
  --signal: oklch(0.70 0.25 340);          /* magenta */
  --signal-glow: oklch(0.80 0.18 320);     /* hot pink */
  --lilac: oklch(0.82 0.10 310);
  --violet: oklch(0.55 0.22 305);
  --nebula-blue: oklch(0.62 0.20 275);
  --deep-blue: oklch(0.38 0.15 270);

  /* CTA */
  --crimson: oklch(0.58 0.21 25);
  --crimson-deep: oklch(0.48 0.20 22);

  /* Nebula gradient — used many times */
  --grad-nebula: linear-gradient(135deg, var(--signal) 0%, var(--violet) 45%, var(--nebula-blue) 100%);
  --grad-aurora-soft: linear-gradient(135deg, var(--signal-glow) 0%, var(--lilac) 50%, var(--nebula-blue) 100%);
  --grad-spine: linear-gradient(to bottom, var(--signal) 0%, var(--signal-glow) 30%, var(--lilac) 65%, var(--nebula-blue) 100%);

  /* Type */
  --font: "Lato", ui-sans-serif, system-ui, -apple-system, sans-serif;

  /* Standardized section heading scale — all section h2s use this */
  --fs-h2: clamp(2rem, 4vw, 3.4rem);
  /* h3 token — applies to all sub-section h3s (services CTA, friction cards, etc.) */
  --fs-h3: clamp(1.3rem, 2vw, 1.7rem);
  /* h4 token — not used in markup yet, but pre-defined so anything added later
     picks up the same scale automatically. */
  --fs-h4: clamp(1.05rem, 1.4vw, 1.3rem);
  /* Body & caption tokens — responsive, used everywhere. */
  --fs-body: clamp(1.05rem, 1.3vw, 1.2rem);
  --fs-caption: 0.9rem;
  /* Lead/pull-quote scale — sits between body and h3 for testimonial quotes, hero subheads, etc. */
  --fs-lead: clamp(1.1rem, 1.5vw, 1.35rem);
  /* Uppercase micro labels (eyebrows, stage labels, tiny meta) — single token. */
  --fs-micro: 0.72rem;

  --maxw: 1240px;
  --pad: clamp(1.25rem, 4vw, 2rem);
}

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

html { scroll-behavior: smooth; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--fg);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

::selection { background: oklch(0.70 0.25 340 / 0.35); color: var(--fg); }

h1, h2, h3, h4 {
  font-family: var(--font);
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1.02;
  text-wrap: balance;
}
/* Base sizes for h3 and h4 so any future h3/h4 in the markup automatically
   picks up the standardized scale. Section-specific rules can still override. */
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }

p { text-wrap: pretty; }

a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: 0; background: 0; color: inherit; }
img { display: block; max-width: 100%; }

/* =========================================================
   Global cosmic background — fixed layers
   ========================================================= */

.cosmos {
  position: fixed; inset: 0; z-index: -10; pointer-events: none;
  background:
    radial-gradient(60% 50% at 15% 8%, oklch(0.40 0.22 320 / 0.35), transparent 60%),
    radial-gradient(50% 45% at 88% 22%, oklch(0.38 0.20 280 / 0.30), transparent 60%),
    radial-gradient(70% 60% at 50% 100%, oklch(0.28 0.14 340 / 0.32), transparent 70%),
    var(--bg);
}

.cosmos__stars { position: absolute; inset: 0; }
.cosmos__nebula {
  position: absolute; inset: -20%;
  background:
    radial-gradient(35% 30% at 20% 30%, oklch(0.45 0.22 320 / 0.22), transparent 60%),
    radial-gradient(40% 30% at 80% 70%, oklch(0.40 0.20 275 / 0.20), transparent 60%);
  filter: blur(20px);
  opacity: 0.9;
  will-change: transform;
}

.grain {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: 0.04; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

/* =========================================================
   Reusable bits
   ========================================================= */

.container { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--pad); }

.eyebrow {
  font-size: var(--fs-micro);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--fg-muted);
  font-weight: 600;
  display: inline-flex; align-items: center;
}
.eyebrow--plain::before { display: none; }

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.5; transform: scale(0.75); }
}

.nebula-text {
  background: linear-gradient(110deg,
    oklch(0.62 0.25 340) 0%,    /* rich magenta */
    oklch(0.68 0.21 25) 28%,    /* deep sunset */
    oklch(0.76 0.17 58) 50%,    /* cosmic orange — warm, not screaming */
    oklch(0.66 0.23 320) 76%,   /* hot magenta */
    oklch(0.60 0.22 285) 100%); /* deep violet */
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  background-size: 200% 200%;
  animation: shimmer 7s ease-in-out infinite;
}
@keyframes shimmer {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}

.btn {
  display: inline-flex; align-items: center; gap: 0.6rem;
  height: 52px; padding: 0 1.5rem;
  border-radius: 999px;
  font-size: 0.96rem; font-weight: 600;
  transition: transform 0.25s ease, background 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
  white-space: nowrap;
  position: relative;
}
/* Hover hit-area guard: extends the hit area a few pixels in every
   direction. Without this, the translateY(-2px) lift moves the bottom
   edge of the .btn up past the cursor at the very edge, triggering a
   un-hover → drop → re-hover flicker loop. The pseudo travels with the
   .btn so the effective hit area covers both the rest and lifted state. */
.btn::after {
  content: "";
  position: absolute;
  inset: -4px -2px -6px -2px;
  pointer-events: auto;
  border-radius: inherit;
  z-index: 0;
}
.btn > * { position: relative; z-index: 1; }
.btn:hover { transform: translateY(-2px); }
.btn svg { transition: transform 0.25s ease; }
.btn:hover svg { transform: translateX(3px); }

.btn--primary {
  background: linear-gradient(135deg, var(--crimson) 0%, var(--crimson-deep) 100%);
  color: white;
  box-shadow: 0 8px 24px -8px oklch(0.55 0.20 25 / 0.55), inset 0 1px 0 0 oklch(1 0 0 / 0.18);
}
.btn--primary:hover { box-shadow: 0 12px 32px -8px oklch(0.55 0.20 25 / 0.7), inset 0 1px 0 0 oklch(1 0 0 / 0.22); }

.btn--ghost {
  background: oklch(1 0 0 / 0.04);
  border: 1px solid var(--border-strong);
  color: var(--fg);
  backdrop-filter: blur(8px);
}
.btn--ghost:hover { background: oklch(1 0 0 / 0.08); border-color: var(--signal); }

.btn--signal {
  background: var(--grad-nebula);
  color: white;
  box-shadow: 0 8px 24px -8px oklch(0.55 0.22 305 / 0.6), inset 0 1px 0 0 oklch(1 0 0 / 0.2);
}

/* Reveal — content always visible by default; JS still adds .is-visible for
   any hooks, but we don't gate visibility on a transition (some embedded
   contexts throttle transitions and leave content stuck at opacity:0). */
.reveal { opacity: 1; transform: none; }
.reveal.is-visible { /* hook class — no visual override needed */ }

section { position: relative; }
.section-divider {
  height: 1px;
  background: linear-gradient(to right, transparent, var(--border-strong) 20%, var(--border-strong) 80%, transparent);
  max-width: var(--maxw); margin: 0 auto;
}

/* =========================================================
   Header
   ========================================================= */

.header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  transition: background 0.3s ease, backdrop-filter 0.3s ease, border-color 0.3s ease;
  border-bottom: 1px solid transparent;
}
.header.is-scrolled {
  background: oklch(0.13 0.06 285 / 0.7);
  backdrop-filter: blur(16px) saturate(140%);
  border-bottom-color: var(--border);
}
.header__inner {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.1rem var(--pad);
  max-width: var(--maxw); margin: 0 auto;
}
.logo {
  display: inline-flex; align-items: center;
  line-height: 0;
  transition: transform 0.25s ease, filter 0.25s ease;
  position: relative;
}
/* Same hit-area guard as .btn — prevents hover flicker when the logo
   lifts on hover. */
.logo::after {
  content: "";
  position: absolute;
  inset: -6px -4px -8px -4px;
  pointer-events: auto;
  z-index: 0;
}
.logo:hover { transform: translateY(-2px); }
.logo__img {
  height: 20px; width: auto; display: block;
  position: relative; z-index: 1;
}
.logo__img--footer {
  height: 18px;
}

/* =========================================================
   Logo glyph + restrained cosmic hover
   ---------------------------------------------------------
   Two PNGs stack inside .logo__glyph — the white base and a
   crimson overlay that crossfades in on hover.

   The cosmic flourish is intentionally quiet so it never
   competes with the red wordmark:
     • .logo__halo — a slim aurora ribbon at the bottom edge,
       like a horizon glow, that fades in beneath the logo.
     • .logo__glyph::after — a single tiny sparkle that
       twinkles near the infinity loop on hover.
   ========================================================= */
.logo__glyph {
  position: relative;
  display: inline-block;
  line-height: 0;
  isolation: isolate;
}
.logo__img--base {
  transition: opacity 0.35s ease;
}
.logo__img--accent {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}
/* Aurora horizon — a thin, soft, multi-stop gradient ribbon
   sitting just below the wordmark. Bleeds slightly past the
   left/right edges and feathers to transparent at both ends,
   so the glow sits beneath the logo like a cosmic ground
   plane rather than haloing the type itself. */
.logo__halo {
  position: absolute;
  left: -12%;
  right: -12%;
  bottom: -7px;
  height: 8px;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  filter: blur(5px);
  background: linear-gradient(90deg,
    transparent 0%,
    oklch(0.62 0.22 25 / 0.55) 28%,
    oklch(0.70 0.18 320 / 0.55) 52%,
    oklch(0.58 0.18 290 / 0.45) 74%,
    transparent 100%);
  transform: scaleX(0.7);
  transform-origin: center;
  transition:
    opacity 0.45s ease,
    transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Single sparkle — a tiny pinpoint of light positioned roughly
   over the infinity loop. Idle: invisible. On hover: twinkles
   in then keeps a slow pulse. Kept small and crimson-tinged
   so it reads as part of the logo, not decoration on top. */
/* Sparkle removed — the halo ribbon underneath the logo carries
   the cosmic flourish on its own. Belt-and-suspenders: kill any
   pseudo-element on .logo__glyph at idle AND on hover, with
   !important so a stale cached stylesheet can't reintroduce it. */
.logo__glyph::after,
.logo:hover .logo__glyph::after {
  content: none !important;
  display: none !important;
  opacity: 0 !important;
  animation: none !important;
  background: none !important;
  box-shadow: none !important;
}
.logo:hover .logo__img--base {
  opacity: 0;
}
.logo:hover .logo__img--accent {
  opacity: 1;
}
.logo:hover .logo__halo {
  opacity: 1;
  transform: scaleX(1);
}
.nav { display: none; gap: 2rem; align-items: center; font-size: 0.93rem; }
/* Plain nav links (not the CTA pill) get the same playful lift as the
   Looptimize logo on hover, plus a soft violet glow and an underline
   that draws in from the center. The overshoot bezier on transform
   gives a small "pop" instead of a flat slide. */
.nav a:not(.cta) {
  position: relative;
  display: inline-block;
  color: var(--fg-muted);
  transition:
    color 0.25s ease,
    transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1.00),
    text-shadow 0.25s ease;
  will-change: transform;
}
/* Hit-area guard — same trick used by .logo and .btn. The link lifts by
   2px on hover; without an extended hover region the cursor can fall out
   from under the link the moment it shifts, releasing :hover and bouncing
   the element back down, which reads as a jitter at the boundary. This
   pseudo extends the hoverable region a few pixels in every direction so
   the cursor stays inside as the link lifts. */
.nav a:not(.cta)::before {
  content: "";
  position: absolute;
  inset: -8px -6px -10px -6px;
  pointer-events: auto;
  z-index: 0;
}
.nav a:not(.cta)::after {
  content: "";
  position: absolute;
  left: 50%;
  right: 50%;
  bottom: -6px;
  height: 1px;
  background: linear-gradient(90deg,
    transparent,
    oklch(0.86 0.16 320 / 0.9),
    transparent);
  opacity: 0;
  pointer-events: none;
  transition:
    left 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    right 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.2s ease;
}
.nav a:not(.cta):hover,
.nav a:not(.cta):focus-visible {
  color: var(--fg);
  transform: translateY(-2px);
  text-shadow: 0 6px 18px oklch(0.72 0.18 320 / 0.55);
}
.nav a:not(.cta):hover::after,
.nav a:not(.cta):focus-visible::after {
  left: 0;
  right: 0;
  opacity: 1;
}
.nav a.cta {
  height: 38px; padding: 0 1.2rem; border-radius: 999px;
  display: inline-flex; align-items: center;
  background: linear-gradient(135deg, var(--crimson) 0%, var(--crimson-deep) 100%);
  color: white;
  font-weight: 600;
  box-shadow: 0 6px 18px -8px oklch(0.55 0.20 25 / 0.55), inset 0 1px 0 0 oklch(1 0 0 / 0.18);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
  position: relative;
}
.nav a.cta::after {
  content: "";
  position: absolute;
  inset: -4px -2px -6px -2px;
  pointer-events: auto;
  z-index: 0;
}
.nav a.cta:hover {
  transform: translateY(-2px);
  color: white;
  box-shadow: 0 10px 26px -8px oklch(0.55 0.20 25 / 0.7), inset 0 1px 0 0 oklch(1 0 0 / 0.22);
}
@media (min-width: 800px) { .nav { display: flex; } }

/* =========================================================
   Mobile menu — hamburger toggle + slide-down panel
   ========================================================= */

.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  background: transparent; border: 1px solid var(--border);
  border-radius: 12px; color: var(--fg); cursor: pointer;
  transition: background 0.25s ease, border-color 0.25s ease, transform 0.25s ease;
  position: relative; z-index: 102;
}
.nav-toggle:hover { background: oklch(1 0 0 / 0.04); border-color: var(--border-strong); }
.nav-toggle:active { transform: scale(0.96); }
.nav-toggle__bars {
  display: inline-block; position: relative; width: 20px; height: 14px;
}
.nav-toggle__bars span {
  position: absolute; left: 0; right: 0; height: 1.6px; border-radius: 2px;
  background: currentColor;
  transition: transform 0.32s cubic-bezier(0.6, -0.2, 0.3, 1.4),
              opacity 0.2s ease, top 0.32s ease;
}
.nav-toggle__bars span:nth-child(1) { top: 0; }
.nav-toggle__bars span:nth-child(2) { top: 50%; transform: translateY(-50%); }
.nav-toggle__bars span:nth-child(3) { top: calc(100% - 1.6px); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(1) {
  top: 50%; transform: translateY(-50%) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(2) {
  opacity: 0; transform: translateY(-50%) scaleX(0);
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(3) {
  top: 50%; transform: translateY(-50%) rotate(-45deg);
}

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

/* On mobile the .nav becomes a slide-down panel pinned under the header.
   The .is-open class flips display and animates each link in with a small
   staggered fade-up so it doesn't read as a flat dropdown. */
@media (max-width: 799.98px) {
  .nav {
    position: fixed; top: 0; left: 0; right: 0;
    /* leave room below the header line; padding-top pushes content past the bar */
    padding: 5.2rem var(--pad) 2rem;
    background: oklch(0.13 0.06 285 / 0.94);
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border-bottom: 1px solid var(--border);
    flex-direction: column; align-items: stretch; gap: 0.25rem;
    font-size: 1rem;
    z-index: 101;
    /* hidden by default — slid up + faded */
    opacity: 0; transform: translateY(-8px); pointer-events: none;
    transition: opacity 0.28s ease, transform 0.32s cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  .nav.is-open {
    display: flex;
    opacity: 1; transform: translateY(0); pointer-events: auto;
  }
  .nav a:not(.cta) {
    padding: 0.95rem 0.25rem;
    border-bottom: 1px solid var(--border);
    transform: none;
    opacity: 0;
  }
  .nav.is-open a:not(.cta) {
    animation: navItemIn 0.42s cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
  }
  .nav.is-open a:not(.cta):nth-child(1) { animation-delay: 0.06s; }
  .nav.is-open a:not(.cta):nth-child(2) { animation-delay: 0.11s; }
  .nav.is-open a:not(.cta):nth-child(3) { animation-delay: 0.16s; }
  .nav.is-open a:not(.cta):nth-child(4) { animation-delay: 0.21s; }
  .nav a:not(.cta):hover,
  .nav a:not(.cta):focus-visible { transform: translateY(0); }
  .nav a:not(.cta)::after { display: none; }
  .nav a.cta {
    margin-top: 1rem; height: 48px; padding: 0 1.4rem;
    justify-content: center; font-size: 0.95rem;
    opacity: 0;
  }
  .nav.is-open a.cta {
    animation: navItemIn 0.42s cubic-bezier(0.2, 0.7, 0.3, 1) 0.26s forwards;
  }

  /* When the menu is open, lock the body scroll via .nav-open on <html> */
  html.nav-open, html.nav-open body { overflow: hidden; }

  /* While the panel is up, give the header a solid backdrop so the bars
     don't sit on top of the logo against an open menu */
  .header:has(.nav.is-open) {
    background: oklch(0.13 0.06 285 / 0.94);
    backdrop-filter: blur(20px) saturate(140%);
    border-bottom-color: var(--border);
  }
}

@keyframes navItemIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .nav, .nav a, .nav-toggle__bars span { transition: none !important; animation: none !important; }
  .nav a:not(.cta), .nav a.cta { opacity: 1 !important; }
}

/* =========================================================
   Hero
   ========================================================= */

.hero { padding: 7rem 0 4.5rem; min-height: 86vh; display: flex; align-items: center; position: relative; overflow: hidden; }

.hero__grid {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(to right, oklch(1 0 0 / 0.04) 1px, transparent 1px),
    linear-gradient(to bottom, oklch(1 0 0 / 0.04) 1px, transparent 1px);
  background-size: 80px 80px;
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, black 30%, transparent 90%);
          mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, black 30%, transparent 90%);
  pointer-events: none;
}

/* Cosmic curved road. SVG sits above the shader / grid (z 0) but below the
   planet illustration and all hero content. Static geometry (body fill,
   edge strokes, dashed spine, glow) lives in the SVG; hero-road.js writes
   the body + edge `d` attributes at init from offset samples of the
   centerline bezier, then continuously spawns particles along the curve. */
.hero__road {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
}
/* Dashed centerline crawls toward the vanishing point at ~1 cycle / 1.2s. */
#roadSpine {
  animation: road-spine-flow 1.2s linear infinite;
}
@keyframes road-spine-flow {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: -22; }   /* one dash (10) + gap (12) = 22 */
}
@media (prefers-reduced-motion: reduce) {
  #roadSpine { animation: none; }
}

/* Liquid shader background.
   Sits at the bottom of the hero stack — behind the dotted grid, the planet
   SVG, and the text (.hero__content has z-index: 2). The shader writes its
   own edge falloffs (bottom, top, sides) so the canvas's rectangle never
   shows; transparent canvas lets the cosmos + starfield show through.
   pointer-events:none keeps CTAs clickable. */
.hero__shader {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
  background: transparent;
}

/* width: 100% forces this flex item to actually stretch to the full container
   width inside .hero (which is display:flex). Without it, the grid sizes to
   its intrinsic content (~920px) and margin:auto centers that smaller block,
   so the eyebrow + h1 land ~160px to the right of the header's logo / every
   other section's container edge. width:100% + the existing max-width cap +
   margin:auto restores the standard container alignment. */
.hero__content { position: relative; z-index: 2; max-width: 980px; width: 100%; }

.hero h1 {
  font-size: clamp(2.6rem, 6.5vw, 5.2rem);
  margin-top: 1.4rem;
  text-wrap: wrap; /* override global `balance` so lines wrap only on overflow */
}

.hero__body {
  margin-top: 1.8rem;
  font-size: var(--fs-body);
  color: var(--fg-muted);
  max-width: 620px;
  line-height: 1.55;
}

.hero__ctas {
  margin-top: 2.5rem;
  display: flex; flex-wrap: wrap; gap: 0.75rem;
}

.hero__pillars {
  margin-top: 3.5rem;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 1.5rem;
  max-width: 760px;
  padding-top: 2rem;
  border-top: 1px solid var(--border);
}
.hero__pillar { font-size: 0.85rem; }
.hero__pillar-label {
  font-size: 0.7rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--signal); font-weight: 700;
  display: flex; align-items: center; gap: 0.5rem;
}
.hero__pillar-label::before {
  content: ""; width: 14px; height: 1px;
  background: linear-gradient(to right, var(--signal), transparent);
}
.hero__pillar-text { margin-top: 0.5rem; color: var(--fg-muted); line-height: 1.5; }

/* Scroll cue */
.scroll-cue {
  position: absolute; bottom: 2rem; left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 0.5rem;
  font-size: 0.7rem; letter-spacing: 0.25em; text-transform: uppercase;
  color: var(--fg-dim);
}
.scroll-cue__line {
  width: 1px; height: 36px;
  background: linear-gradient(to bottom, transparent, var(--signal), transparent);
  background-size: 100% 200%;
  animation: scrollLine 2.4s ease-in-out infinite;
}
@keyframes scrollLine {
  0% { background-position: 0% -100%; }
  100% { background-position: 0% 200%; }
}

/* =========================================================
   Clients
   ========================================================= */

.clients { padding: 5rem 0; position: relative; }
.clients .eyebrow { display: flex; justify-content: center; }
.marquee {
  margin-top: 3rem;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, black 12%, black 88%, transparent);
          mask-image: linear-gradient(to right, transparent, black 12%, black 88%, transparent);
}
.marquee__track {
  display: flex; align-items: center;
  width: max-content;
  animation: marquee 45s linear infinite;
}
.marquee__track img {
  height: 34px; width: auto;
  max-width: 116px;
  object-fit: contain;
  /* Spacing lives on the items (not flex `gap`) so the duplicated set is
     perfectly periodic and translateX(-50%) loops with no jump. */
  margin-right: 4rem;
  opacity: 0.55;
  filter: brightness(0) invert(1);
  transition: opacity 0.3s;
}
.marquee__track img:hover { opacity: 1; }
@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* =========================================================
   Approach
   ========================================================= */

.approach { padding: 8rem 0; position: relative; overflow: hidden; }

/* -- Approach: animated glass card -- */
@property --approach-ring-angle { syntax: "<angle>"; inherits: false; initial-value: 0deg; }

.approach__card {
  position: relative; overflow: hidden;
  border-radius: 30px;
  padding: clamp(2.2rem, 5vw, 4.5rem);
  min-height: 560px;
  display: flex; align-items: center;
  /* Night sky: cosmic-red bloom upper-left, teal horizon glow rising from the mountains */
  background:
    radial-gradient(120% 90% at 16% 6%, oklch(0.40 0.15 26 / 0.55) 0%, transparent 48%),
    radial-gradient(120% 80% at 78% 116%, oklch(0.50 0.11 200 / 0.55) 0%, transparent 60%),
    radial-gradient(90% 70% at 84% -10%, oklch(0.42 0.13 50 / 0.4) 0%, transparent 55%),
    linear-gradient(178deg, oklch(0.17 0.06 268 / 0.97) 0%, oklch(0.13 0.05 262 / 0.97) 52%, oklch(0.15 0.06 240 / 0.97) 100%);
  -webkit-backdrop-filter: blur(4px) saturate(110%);
          backdrop-filter: blur(4px) saturate(110%);
  border: 1px solid oklch(1 0 0 / 0.1);
  box-shadow:
    0 30px 80px -42px oklch(0.05 0.04 285 / 0.92),
    inset 0 1px 0 0 oklch(1 0 0 / 0.14),
    inset 0 -1px 0 0 oklch(0 0 0 / 0.2);
  /* pointer-driven 3D tilt (vars set by approach-card.js) */
  transform: perspective(1300px) rotateX(var(--ap-rx, 0deg)) rotateY(var(--ap-ry, 0deg));
  transform-style: preserve-3d;
  transition: box-shadow 0.5s ease, border-color 0.5s ease;
  will-change: transform;
  outline: none;
}
.approach__card:hover, .approach__card:focus-visible {
  border-color: oklch(0.84 0.14 64 / 0.4);
  box-shadow:
    0 48px 110px -46px oklch(0.45 0.16 40 / 0.6),
    inset 0 1px 0 0 oklch(1 0 0 / 0.18),
    inset 0 -1px 0 0 oklch(0 0 0 / 0.2);
}

/* -- Scene + parallax layers -- */
.ap-scene { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; pointer-events: none; transform-style: preserve-3d; }
.ap-layer { position: absolute; inset: 0; will-change: transform; opacity: 0; transition: opacity 1s ease; }
/* staggered fade-in once the card enters view (opacity only — never fights the JS transform) */
.approach__card.is-visible .ap-layer { opacity: 1; }
.approach__card.is-visible .ap-layer:nth-child(1) { transition-delay: 0.05s; }
.approach__card.is-visible .ap-layer:nth-child(2) { transition-delay: 0.12s; }
.approach__card.is-visible .ap-layer:nth-child(3) { transition-delay: 0.18s; }
.approach__card.is-visible .ap-layer:nth-child(4) { transition-delay: 0.24s; }
.approach__card.is-visible .ap-layer:nth-child(5) { transition-delay: 0.30s; }
.approach__card.is-visible .ap-layer:nth-child(6) { transition-delay: 0.42s; }
.approach__card.is-visible .ap-layer:nth-child(7) { transition-delay: 0.52s; }
.approach__card.is-visible .ap-layer:nth-child(8) { transition-delay: 0.60s; }

/* slow-drifting sky glow — subtle, blended, never an isolated blob */
.ap-aurora {
  position: absolute; inset: -25%;
  background:
    radial-gradient(38% 44% at 18% 12%, oklch(0.52 0.16 28 / 0.20), transparent 68%),
    radial-gradient(40% 48% at 72% 60%, oklch(0.46 0.13 250 / 0.20), transparent 72%),
    radial-gradient(44% 40% at 60% 18%, oklch(0.58 0.10 60 / 0.10), transparent 74%),
    radial-gradient(40% 46% at 86% 30%, oklch(0.50 0.12 300 / 0.14), transparent 70%);
  filter: blur(36px) saturate(110%);
  animation: apAurora 46s ease-in-out infinite alternate;
}
@keyframes apAurora {
  0%   { transform: translate3d(-2.5%, -1.5%, 0) scale(1.05) rotate(0deg); }
  50%  { transform: translate3d(1.5%, 1%, 0)     scale(1.1)  rotate(2.5deg); }
  100% { transform: translate3d(2.5%, 2.5%, 0)   scale(1.14) rotate(5deg); }
}

/* banded gas giant, upper-right */
.ap-planet {
  position: absolute; width: clamp(160px, 23vw, 280px); height: auto; aspect-ratio: 1;
  right: -5%; top: -15%;
  filter: drop-shadow(0 0 36px oklch(0.62 0.16 44 / 0.4));
  animation: apBob 12s ease-in-out infinite;
}
@keyframes apBob { 0%,100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-10px) rotate(1deg); } }

/* small moon, upper-left */
.ap-moon {
  position: absolute; width: clamp(46px, 6vw, 74px); height: auto; aspect-ratio: 1;
  left: 7%; top: 9%;
  filter: drop-shadow(0 0 16px oklch(0.70 0.16 50 / 0.45));
  animation: apBob 15s ease-in-out infinite;
}

/* Kurzgesagt planet surface — stacked terrain ridges curving up into the
   teal horizon glow. Each ridge is a flat silhouette with a rim-lit top edge;
   they sit on .ap-layer wrappers so they inherit the pointer parallax. */
.ap-terrain {
  position: absolute; left: -5%; bottom: -5%;
  width: 110%; height: auto;
}

/* GLSL animated hills — replaces the static .ap-terrain ridges.
   Bottom 20% of the card, full width, opacity fade-in matched to the other
   .ap-layer transitions. The canvas inside is sized by ap-hills.js via a
   ResizeObserver on this container. pointer-events:none ensures CTAs and
   card hover/focus still receive their events through the canvas. */
.ap-hills {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 40%;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  transition: opacity 1s ease;
}
.approach__card.is-visible .ap-hills { opacity: 1; transition-delay: 0.30s; }
.ap-hills canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* comet canvas overlay — decoupled from layout, drawn per-frame in comet.js */
.ap-comet {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none; z-index: 4;
}

/* contrast veil — gently darkens the centre band so text stays crisp,
   while leaving the bright corners (planet, moon) untouched */
.ap-veil {
  position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background:
    radial-gradient(120% 78% at 46% 48%, oklch(0.08 0.04 270 / 0.5) 0%, transparent 66%);
}

/* screen-print grain — only at the edges/corners, fading to none toward the centre */
.ap-grain {
  position: absolute; inset: 0; pointer-events: none; z-index: 4;
  opacity: 0.42; mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
  -webkit-mask-image: radial-gradient(118% 100% at 50% 50%, transparent 34%, rgba(0,0,0,0.5) 64%, black 92%);
          mask-image: radial-gradient(118% 100% at 50% 50%, transparent 34%, rgba(0,0,0,0.5) 64%, black 92%);
}

/* stars — warm, screen-print whites */
.ap-star {
  position: absolute; color: oklch(0.91 0.07 72);
  animation: apTwinkle var(--tw, 3s) ease-in-out infinite;
}
@keyframes apTwinkle { 0%,100% { opacity: .25; transform: scale(0.7); } 50% { opacity: 1; transform: scale(1); } }

/* cursor lens — a soft circular region that lifts the saturation/brightness of
   the scene painted behind it (text sits above, so it stays untouched) */
.ap-lens {
  position: absolute; top: 0; left: 0; width: 325px; height: 325px; border-radius: 50%;
  pointer-events: none; z-index: 3;
  transform: translate(-9999px, -9999px);
  -webkit-backdrop-filter: saturate(2.1) brightness(1.07) contrast(1.04);
          backdrop-filter: saturate(2.1) brightness(1.07) contrast(1.04);
  -webkit-mask-image: radial-gradient(circle at center, #000 30%, transparent 68%);
          mask-image: radial-gradient(circle at center, #000 30%, transparent 68%);
  visibility: hidden; opacity: 0; transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}
.approach__card:hover .ap-lens, .approach__card:focus-visible .ap-lens { visibility: visible; opacity: 1; transition: opacity 0.4s ease, visibility 0s; }

/* border shine — a concentrated highlight that rides the card's edge.
   Its angle (--ap-ring) is driven by approach-card.js: it tracks the cursor
   while hovering, then spins off with an explosive ease-out back to rest. */
@property --ap-ring { syntax: "<angle>"; inherits: false; initial-value: -96deg; }
.ap-ring {
  position: absolute; inset: 0; border-radius: 30px;
  padding: 1.4px; pointer-events: none; z-index: 2; opacity: 0;
  background: conic-gradient(from var(--ap-ring, -96deg),
    transparent 0deg,
    oklch(0.74 0.20 340 / 0) 46deg,
    oklch(0.80 0.18 330 / 0.85) 82deg,
    oklch(0.95 0.13 60 / 1) 96deg,
    oklch(0.80 0.18 330 / 0.85) 110deg,
    oklch(0.74 0.20 340 / 0) 146deg,
    transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  filter: drop-shadow(0 0 7px oklch(0.82 0.16 336 / 0.55));
  transition: opacity 0.45s ease;
}
.approach__card:hover .ap-ring, .approach__card:focus-visible .ap-ring { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .ap-aurora, .ap-planet, .ap-moon, .ap-star { animation: none !important; }
  .ap-comet { display: none; }
  .approach__card { transform: none !important; }
}

/* =========================================================
   Hero illustration — right-column planet cluster
   (replaces previous wave/particle layers)
   ========================================================= */

/* LAYER 2 (illustration): absolutely positioned inside .hero__container.
   Below 900px it falls behind text at low opacity. On wide viewports it
   anchors to a left edge equal to the text layer's max-width (990px), so the
   illustration starts exactly at the text's right boundary. right: -14vw
   pulls the planet past the viewport's right edge for the cinematic crop the
   spec calls for. */
/* Stacking: above the road (z 1) so the planets, moons, orbital arcs, and
   comet trails all render in front of the road body, edges, dashed spine,
   and particle stream. The text in .hero__content (z 2) also sits above the
   road; the planet illustration and the text don't overlap visually so they
   can share the same stacking layer. */
.hero__illo {
  position: absolute; top: 0; right: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 2;
  opacity: 0.4;
}
@media (min-width: 900px) {
  .hero__illo {
    /* Bumped from 33% → 40%: gives the planet cluster more visual presence so it
       reads as the hero's focal point rather than a small inset graphic. Safe at
       all wide breakpoints — leftmost visible SVG content (orange planet at
       viewBox x≈846 of 720..1600 → 14.3% from SVG left) stays well clear of
       the text column, and rightmost content (small ringed planet right edge
       at ~84% from SVG left) stays inside the SVG box, so nothing crops. */
    width: 40%;
    opacity: 1;
  }
}


/* Text-dominant 60/40 grid so the headline's gradient phrase
   "perfect customer journeys." never breaks across two lines. */
@media (min-width: 900px) {
  .hero__content {
    max-width: var(--maxw);
    display: grid;
    grid-template-columns: minmax(0, 2.33fr) minmax(0, 1fr);
    column-gap: 0;
    align-items: center;
  }
  .hero__content > * { grid-column: 1; min-width: 0; }
  /* Headline alone spans the full row width so the nowrap gradient phrase
     never overflows. width: max-content lets it size to its content, capped
     at 100% of the row. */
  .hero h1 {
    grid-column: 1 / -1;
    width: max-content;
    max-width: 100%;
  }
  .hero__body { max-width: 600px; }
}

/* ---- Atmospheric blobs: hidden.
   The .illo-blobs group inside the planet SVG paints dim violet atmosphere
   behind the planet, but it's constrained to the SVG's box (right ~40% of
   the hero on wide screens). That creates a visible vertical "cut" along
   the SVG's left edge — violet wash inside the box, dark cosmos outside.
   Hide the group; the planet sits cleanly on the cosmos backdrop. */
.illo-blobs { display: none; }
.illo-blob { }
.illo-blob--1 { animation: blobDriftA 56s ease-in-out infinite alternate; transform-origin: center; }
.illo-blob--2 { animation: blobDriftB 48s ease-in-out -14s infinite alternate; transform-origin: center; }
.illo-blob--3 { animation: blobDriftC 60s ease-in-out -28s infinite alternate; transform-origin: center; }
@keyframes blobDriftA { from { transform: translateX(-14px); } to { transform: translateX(18px); } }
@keyframes blobDriftB { from { transform: translateX(16px); } to { transform: translateX(-12px); } }
@keyframes blobDriftC { from { transform: translateX(-10px); } to { transform: translateX(14px); } }

/* ---- Hero planet body + rings: rotate around planet center.
   SVG transform attributes set the baseline tilt; CSS animation rotates ON TOP
   via a wrapper class on a different element. We declare keyframes that
   interpolate from the SVG's inline transform value — so we set explicit
   `from` and `to` rotations that include the tilt. ---- */
.illo-planet-spin {
  transform-origin: 1180px 450px;
  animation: planetSpin 170s linear infinite;
  will-change: transform;
}
@keyframes planetSpin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Rings: each pair (back + front) tilts at the baseline angle and slowly
   rotates. We use the wrapper <g class="illo-ringA"> with inline
   transform="rotate(18 1180 450)" — CSS keyframes preserve that tilt by
   matching it in the from state. */
.illo-ringA, .illo-ringB {
  transform-origin: 1180px 450px;
  will-change: transform;
}
.illo-ringA { animation: ringSpinA 90s linear infinite; }
.illo-ringB { animation: ringSpinB 110s linear infinite; }
@keyframes ringSpinA {
  from { transform: rotate(18deg); }
  to   { transform: rotate(378deg); }
}
@keyframes ringSpinB {
  from { transform: rotate(-22deg); }
  to   { transform: rotate(-382deg); }
}

/* ---- Side planets: smooth vertical float.
   Pure CSS keyframes (the previous SMIL animateMotion drift was removed) so the
   two side planets drift up and down on their own slow timings. Different
   durations + a negative delay on the orange planet keep them desynchronized
   so they read as independent objects, not metronomes. ---- */
.illo-small  { animation: planetFloat 4s   ease-in-out infinite alternate; will-change: transform; }
.illo-orange { animation: planetFloat 5.2s ease-in-out -1.6s infinite alternate; will-change: transform; }
/* Hero planet (the big ringed one) floats too — slower than the side planets
   to match its larger visual mass. The satellite is on the same animation so
   its orbit stays anchored to the planet's bobbing center rather than drifting
   in/out as the planet moves. */
.illo-hero,
.illo-satellite { animation: planetFloat 9s ease-in-out infinite alternate; will-change: transform; }
@keyframes planetFloat {
  from { transform: translateY(-18px); }
  to   { transform: translateY( 18px); }
}

/* ---- Star twinkles: 6 stars, independent timings (3-6s) ---- */
.illo-star--tw1 { animation: starTw 4.2s ease-in-out infinite; }
.illo-star--tw2 { animation: starTw 5.6s ease-in-out -1.3s infinite; }
.illo-star--tw3 { animation: starTw 3.4s ease-in-out -2.1s infinite; }
.illo-star--tw4 { animation: starTw 5.1s ease-in-out -0.7s infinite; }
.illo-star--tw5 { animation: starTw 3.9s ease-in-out -2.6s infinite; }
.illo-star--tw6 { animation: starTw 4.7s ease-in-out -3.4s infinite; }
@keyframes starTw {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 0.85; }
}

/* ===== Orbital arcs — drawn-in / drawn-out as if tracing an orbit =====
   The arc sweeps from one end to the other (left-to-right "draw in"), holds
   briefly at full visibility, then sweeps off again — like an orbital
   horizon line briefly revealing itself in the space scene before
   dematerializing. Combined with the comet trails below, the static arcs
   no longer feel pinned in place. */
.hero__arc {
  pointer-events: none;
  pathLength: 1000;
  /* dash = 1000, gap = 1000, normalized via pathLength="1000".
     dashoffset cycles  +1000 → 0 → -1000 to draw in then erase out. */
  stroke-dasharray: 1000 1000;
  stroke-dashoffset: 1000;
  will-change: stroke-dashoffset, opacity;
}
.hero__arc--1 {
  animation: hero-arc-sweep-1 8.4s linear infinite;
}
.hero__arc--2 {
  animation: hero-arc-sweep-2 12.6s linear infinite;
  /* No negative delay here — a mid-cycle start was putting arc 2 in its
     erase phase on first paint, which read as “the line starts but fades out
     within a second.” Cycle-length difference (8.4s vs 12.6s) handles the
     drift between the two arcs naturally after the first cycle. */
}
@keyframes hero-arc-sweep-1 {
  0%   { stroke-dashoffset:  1000; opacity: 0; }
  5%   { opacity: 0.55; }
  18%  { stroke-dashoffset:     0; opacity: 0.55; }   /* fully drawn */
  25%  { stroke-dashoffset:     0; opacity: 0.55; }   /* held — moment of visibility */
  38%  { stroke-dashoffset: -1000; opacity: 0.55; }   /* erased away */
  43%  { opacity: 0; }
  100% { stroke-dashoffset: -1000; opacity: 0; }      /* long dark hold — the next breath is far away */
}
@keyframes hero-arc-sweep-2 {
  0%   { stroke-dashoffset:  1000; opacity: 0; }
  6%   { opacity: 0.60; }
  19%  { stroke-dashoffset:     0; opacity: 0.60; }
  26%  { stroke-dashoffset:     0; opacity: 0.60; }
  39%  { stroke-dashoffset: -1000; opacity: 0.60; }
  44%  { opacity: 0; }
  100% { stroke-dashoffset: -1000; opacity: 0; }
}

/* ===== Orbital trail comets =====
   A bright dash glides along each static arc, fading in as it enters the path,
   gliding across with a soft glow, and fading out before it loops. The two
   trails run on independently-paced cycles so they don't tick together.

   Visual treatment: a near-white thin core stroke is the comet's bright
   point of light; layered drop-shadow filters fan out in a multi-stop
   colored gradient — tight white inner halo, mid violet/magenta bloom,
   deep cool outer glow — giving the comet a cosmic, layered shimmer
   instead of a single flat color. */
.hero__trail {
  stroke-width: 1.1;
  stroke-linecap: round;
  fill: none;
  opacity: 0;
  /* pathLength="1000" normalizes the SVG path so these dash values are
     length-independent. The dash is 70 units; the gap is 1000 so only one
     concentrated point of light exists per cycle. */
  stroke-dasharray: 70 1000;
  stroke-dashoffset: 70;
  pointer-events: none;
  will-change: stroke-dashoffset, opacity;
}
.hero__trail--1 {
  stroke: oklch(0.98 0.04 308);
  filter:
    drop-shadow(0 0 1px   oklch(0.98 0.04 308))
    drop-shadow(0 0 3px   oklch(0.88 0.14 318 / 0.95))
    drop-shadow(0 0 8px   oklch(0.74 0.20 312 / 0.55))
    drop-shadow(0 0 18px  oklch(0.58 0.22 296 / 0.32));
  /* Linear timing on the comet so it moves at a constant cosmic speed —
     a slow-start ease made the head appear stationary right as it was
     fading in, which looked like the comet was glitching out. */
  animation: hero-trail-glide-1 8.4s linear infinite;
}
.hero__trail--2 {
  stroke: oklch(0.98 0.04 326);
  filter:
    drop-shadow(0 0 1px   oklch(0.98 0.04 326))
    drop-shadow(0 0 3px   oklch(0.88 0.16 328 / 0.95))
    drop-shadow(0 0 8px   oklch(0.74 0.22 330 / 0.55))
    drop-shadow(0 0 16px  oklch(0.60 0.24 340 / 0.32));
  animation: hero-trail-glide-2 12.6s linear infinite;
  /* Kept in sync with arc 2 — see note on .hero__arc--2 above. */
}
@keyframes hero-trail-glide-1 {
  0%   { stroke-dashoffset: 70;    opacity: 0; }
  3%   { opacity: 1; }                              /* fully bright by the time it has crossed onto the path */
  38%  { opacity: 1; }                              /* held while gliding the length of the arc */
  44%  { stroke-dashoffset: -1070; opacity: 0; }    /* off the far end — fade out */
  100% { stroke-dashoffset: -1070; opacity: 0; }    /* long dark hold between glides */
}
@keyframes hero-trail-glide-2 {
  0%   { stroke-dashoffset: 70;    opacity: 0; }
  3%   { opacity: 1; }
  38%  { opacity: 1; }
  44%  { stroke-dashoffset: -1070; opacity: 0; }
  100% { stroke-dashoffset: -1070; opacity: 0; }
}

/* Pause all hero SVG CSS keyframe animations when the hero section has scrolled
   off-screen. The .is-offscreen class is toggled by initHeroAnimationGate() in
   looptimize.js, which also calls svg.pauseAnimations() / unpauseAnimations()
   to halt the SMIL animateMotion orbit (the echo-particle). */
.hero.is-offscreen .illo-blob,
.hero.is-offscreen .illo-planet-spin,
.hero.is-offscreen .illo-ringA,
.hero.is-offscreen .illo-ringB,
.hero.is-offscreen .illo-small,
.hero.is-offscreen .illo-orange,
.hero.is-offscreen .illo-satellite,
.hero.is-offscreen [class*="illo-star--tw"],
.hero.is-offscreen .illo-hero,
.hero.is-offscreen .hero__arc,
.hero.is-offscreen .hero__trail {
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  .illo-blob, .illo-planet-spin, .illo-ringA, .illo-ringB,
  .illo-orange-glow, .illo-star, .illo-satellite, .illo-small, .illo-orange,
  .illo-hero, .hero__trail, .hero__arc {
    animation: none !important;
  }
  .hero__arc { stroke-dashoffset: 0; opacity: 0.55; }
}

.approach__inner {
  display: grid; gap: 2.5rem;
  grid-template-columns: 1fr;
  position: relative; z-index: 5;
  width: 100%;
}
.approach__inner :is(h2, p, .eyebrow) { text-shadow: 0 1px 16px oklch(0.08 0.03 270 / 0.6); }
@media (min-width: 900px) {
  .approach__inner { grid-template-columns: 5fr 7fr; gap: 4rem; align-items: start; }
  /* Align the body column to the "Our approach:" heading (past the eyebrow + h2 margin),
     not to the eyebrow at the very top. */
  .approach__inner > div:last-child { margin-top: calc(0.72rem * 1.5 + 1.4rem + 8px); }
}
.approach h2 {
  margin-top: 1.4rem;
  font-size: var(--fs-h2);
}
.approach__body {
  font-size: var(--fs-body);
  color: var(--fg-muted);
  line-height: 1.55;
}
.approach__body em {
  color: var(--fg); font-style: normal;
  font-weight: 600;
}

/* =========================================================
   Momentum
   ========================================================= */

.momentum { padding: 4rem 0 8rem; }
.momentum__inner {
  max-width: 760px;
}
.momentum h2 { font-size: var(--fs-h2); margin-top: 1.4rem; }
/* Body paragraphs inside the momentum block. `:not(.eyebrow)` avoids stealing
   the eyebrow's small-caps sizing — the previous broad `.momentum p` rule had
   higher specificity than `.eyebrow` and broke it. */
.momentum p:not(.eyebrow) { font-size: var(--fs-body); color: var(--fg-muted); line-height: 1.55; margin-top: 1.2rem; }
.momentum p em { font-style: normal; color: var(--fg); font-weight: 600; }

/* =========================================================
   Friction
   ========================================================= */

.friction { padding: 7rem 0; position: relative; }
.friction__intro { max-width: 760px; }
.friction h2 { font-size: var(--fs-h2); margin-top: 1.2rem; }
.friction__body { margin-top: 1.4rem; font-size: var(--fs-body); color: var(--fg-muted); }

.friction__grid {
  margin-top: 4rem;
  display: grid; gap: 1px;
  grid-template-columns: 1fr;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: 20px;
  overflow: hidden;
}
@media (min-width: 600px) { .friction__grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1024px) { .friction__grid { grid-template-columns: repeat(4, 1fr); } }

.friction-card {
  background: oklch(0.18 0.07 290 / 0.6);
  backdrop-filter: blur(6px);
  padding: 2rem 1.6rem 2.2rem;
  position: relative; overflow: hidden;
  transition: background 0.4s ease;
}
.friction-card:hover { background: oklch(0.24 0.10 295 / 0.7); }
.friction-card::after {
  content: "";
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 0; height: 2px;
  background: var(--grad-nebula);
  transition: width 0.5s ease;
}
.friction-card:hover::after { width: 100%; }
.friction-card__top {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 2rem;
}
.friction-card__num {
  font-size: var(--fs-micro); letter-spacing: 0.2em; color: var(--signal); font-weight: 700;
}
.friction-card__glyph {
  width: 56px; height: 56px;
  color: var(--lilac);
  transition: transform 0.5s ease, color 0.3s;
}
.friction-card:hover .friction-card__glyph { transform: scale(1.08); color: var(--signal-glow); }

/* ===== Technical Friction — animated loadscene ===== */
.friction-card__glyph--loadscene {
  width: 72px; height: 72px;
  color: oklch(0.78 0.05 280);
}
.friction-card:hover .friction-card__glyph--loadscene { transform: scale(1.06); color: oklch(0.78 0.05 280); }
.loadscene {
  width: 72px; height: 72px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  user-select: none;
  position: relative;
}
.loadscene__pct {
  font-size: 10px; line-height: 1; letter-spacing: 0.02em;
  color: oklch(0.72 0.04 290);
  font-variant-numeric: tabular-nums;
  display: inline-flex; align-items: baseline;
}
.loadscene__pct [data-pct] { display: inline-block; min-width: 1.7em; text-align: right; }
.loadscene__pctsign { opacity: 0.55; margin-left: 1px; }
.loadscene__track {
  width: 56px; height: 7px;
  background: oklch(0.22 0.05 295);
  border-radius: 999px;
  position: relative;
  overflow: hidden;
  transform-origin: center;
  will-change: transform;
}
.loadscene__fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 0%;
  background: #7C5CFC;
  border-radius: 999px;
  box-shadow: 0 0 4px rgba(124, 92, 252, 0.6);
  transition: background 80ms linear, box-shadow 80ms linear;
}
.loadscene__edge {
  position: absolute; right: 0; top: 0; bottom: 0;
  width: 3px;
  background: rgba(255, 255, 255, 0.9);
  border-radius: 999px;
  filter: blur(0.3px);
  opacity: 1;
  transition: opacity 100ms linear;
}
.loadscene__badge {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 34px;
  height: 14px;
  margin-left: -17px;
  margin-top: -7px;             /* initial center == bar center */
  border-radius: 4px;
  background: rgba(255, 77, 77, 0.9);
  border: 1px solid rgba(255, 77, 77, 0.6);
  color: #fff;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 4px rgba(255, 77, 77, 0.5);
  pointer-events: none;
  opacity: 0;
  transform: translate(0, 0) scale(0);
  z-index: 5;
  will-change: transform, opacity;
}
.loadscene__status {
  width: 88px; max-width: 88px;
  font-size: 9.5px; line-height: 1.2;
  font-family: ui-sans-serif, system-ui, "Helvetica Neue", Arial, sans-serif;
  color: oklch(0.62 0.03 290);
  text-align: center;
  /* Allow the status message to wrap onto two lines so it stays legible
     at a usable font size instead of being squeezed into a 7.5px single
     line with ellipsis. */
  white-space: normal;
  letter-spacing: 0.01em;
  display: flex; justify-content: center; align-items: center; gap: 1px;
  min-height: 24px;
}
.loadscene__cursor {
  display: none;
  width: 4px; height: 7px;
  background: currentColor;
  animation: loadscene-blink 0.7s steps(2) infinite;
}
.loadscene[data-show-cursor="1"] .loadscene__cursor { display: inline-block; }
@keyframes loadscene-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .loadscene__cursor { animation: none; }
}

/* ===== Usability Friction — animated dodge scene ===== */
.friction-card__glyph--dodgescene {
  width: 72px; height: 72px;
  color: oklch(0.78 0.10 290);
}
.friction-card:hover .friction-card__glyph--dodgescene { transform: scale(1.06); color: oklch(0.78 0.10 290); }
.dodgescene {
  position: relative;
  width: 72px; height: 72px;
  --dodge-glow: 0;
  opacity: 0; /* JS drives scene fade */
}
.dodgescene__btn {
  position: absolute;
  left: 36px; top: 12px;       /* anchored at Position A (top-right) */
  width: 36px; height: 12px;
  border-radius: 6px;
  border: 1px solid rgba(124, 92, 252, 0.85);
  background: rgba(124, 92, 252, 0.10);
  box-shadow:
    0 0 4px rgba(124, 92, 252, calc(0.28 + var(--dodge-glow) * 0.55)),
    inset 0 0 3px rgba(180, 160, 255, calc(0.18 + var(--dodge-glow) * 0.35));
  animation: dodge-breath 2.2s cubic-bezier(0.4, 0.0, 0.6, 1.0) infinite;
  will-change: transform, opacity, box-shadow;
}
@keyframes dodge-breath {
  0%, 100% { opacity: 0.85; }
  50% { opacity: 1.0; }
}
.dodgescene__cursor {
  position: absolute;
  left: 0; top: 0;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
  pointer-events: none;
  will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) {
  .dodgescene__btn { animation: none; opacity: 1; }
  .dodgescene { opacity: 1; }
}

/* ===== Cognitive Friction — animated decision-tree scene ===== */
.friction-card__glyph--treescene {
  width: 72px; height: 72px;
  color: oklch(0.78 0.13 285);
}
.friction-card:hover .friction-card__glyph--treescene { transform: scale(1.06); color: oklch(0.78 0.13 285); }
.treescene {
  width: 72px; height: 72px;
  display: block;
  overflow: visible;
}

/* ===== Emotional Friction — animated star-row scene ===== */
.friction-card__glyph--starscene {
  width: 72px; height: 72px;
}
.friction-card:hover .friction-card__glyph--starscene { transform: scale(1.06); }
.starscene {
  width: 72px; height: 72px;
  display: block;
  overflow: visible;
}
.starscene [data-star-inner] {
  will-change: transform;
}
.starscene [data-star-fill] {
  will-change: opacity, filter;
}
.friction-card h3 { font-size: var(--fs-h3); font-weight: 800; letter-spacing: -0.02em; }
.friction-card__desc { margin-top: 0.7rem; color: var(--fg-muted); line-height: 1.55; font-size: var(--fs-caption); }

/* Friction demo */
.friction-demo {
  margin-top: 4rem;
  border-radius: 20px;
  border: 1px solid var(--border);
  background: oklch(0.16 0.06 290 / 0.4);
  padding: 2rem clamp(1rem, 3vw, 2.5rem) 2.5rem;
  position: relative; overflow: hidden;
}
.friction-demo__header {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 1rem;
}
.friction-demo__title { font-weight: 700; font-size: 1.05rem; letter-spacing: -0.01em; }
.friction-demo__sub { color: var(--fg-muted); font-size: var(--fs-caption); max-width: 480px; }
.friction-demo__stage {
  margin-top: 1.6rem;
  position: relative;
  height: 160px;
  border-radius: 14px;
  background:
    radial-gradient(ellipse at 50% 50%, oklch(0.20 0.08 290 / 0.7), oklch(0.13 0.06 285 / 0.9));
  border: 1px solid var(--border);
  overflow: hidden;
  cursor: grab;
  user-select: none;
}
.friction-demo__stage.dragging { cursor: grabbing; }
.friction-demo__track {
  position: absolute; left: 7%; right: 7%; top: 50%; height: 2px; transform: translateY(-50%);
  background: linear-gradient(to right, transparent, oklch(1 0 0 / 0.18) 10%, oklch(1 0 0 / 0.18) 90%, transparent);
}
.friction-demo__start, .friction-demo__end {
  position: absolute; top: 50%; transform: translateY(-50%);
  font-size: 0.65rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--fg-dim);
}
.friction-demo__start { left: 1rem; }
.friction-demo__end { right: 1rem; color: var(--signal); }
.friction-demo__well {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  width: 64px; height: 64px;
  border-radius: 50%;
  background: radial-gradient(circle, oklch(0.40 0.20 320 / 0.5), oklch(0.30 0.15 320 / 0.0) 70%);
}
.friction-demo__well-label {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, calc(-50% + 48px));
  font-size: 0.62rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--fg-dim); white-space: nowrap;
}
.friction-demo__comet {
  position: absolute; top: 50%; left: 7%; transform: translate(-50%, -50%);
  width: 22px; height: 22px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, white, var(--signal-glow) 40%, var(--signal) 80%);
  box-shadow: 0 0 24px var(--signal), 0 0 48px var(--signal-glow);
  cursor: grab;
  z-index: 5;
  transition: box-shadow 0.3s, filter 0.3s;
}
.friction-demo__comet.is-sluggish {
  filter: saturate(0.5) brightness(0.7);
  box-shadow: 0 0 12px oklch(0.50 0.10 290 / 0.5);
}
.friction-demo__comet::before {
  content: ""; position: absolute; top: 50%; right: 100%; transform: translateY(-50%);
  width: 60px; height: 4px;
  background: linear-gradient(to left, var(--signal-glow), transparent);
  border-radius: 4px;
  filter: blur(2px);
}
.friction-demo__readout {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  margin-top: 1rem;
  font-size: 0.78rem; color: var(--fg-muted);
  flex-wrap: wrap;
}
.friction-demo__momentum {
  display: flex; align-items: center; gap: 0.7rem;
  font-variant-numeric: tabular-nums;
}
.friction-demo__meter {
  width: 140px; height: 4px; border-radius: 2px;
  background: oklch(1 0 0 / 0.08); overflow: hidden;
}
.friction-demo__meter-fill {
  height: 100%; width: 100%;
  background: var(--grad-nebula);
  transition: width 0.2s ease;
}
.friction-demo__hint { color: var(--fg-dim); font-size: 0.75rem; }

/* =========================================================
   Services / CJO Suite
   ========================================================= */

.services { padding: 8rem 0; position: relative; }
.services__intro { max-width: 820px; }
.services h2 { font-size: var(--fs-h2); margin-top: 1.2rem; }
.services__body { margin-top: 1.4rem; font-size: var(--fs-body); color: var(--fg-muted); max-width: 700px; }

/* ===== CJO operating model — vertical journey with timeline rail ===== */

/* Per-stage warm accent palette */
.cjo-card[data-stage="0"],
.cjo-rail__node[data-stage="0"] { --stage-accent: #F5C842; }
.cjo-card[data-stage="1"],
.cjo-rail__node[data-stage="1"] { --stage-accent: #F09030; }
.cjo-card[data-stage="2"],
.cjo-rail__node[data-stage="2"] { --stage-accent: #E05E20; }
.cjo-card[data-stage="3"],
.cjo-rail__node[data-stage="3"] { --stage-accent: #C42A10; }

/* ---- Layout shell ---- */
.cjo-journey-wrap {
  margin-top: 4rem;
}
.cjo-journey {
  display: grid;
  grid-template-columns: 80px 1fr;
  align-items: start;
  gap: 0;
}

/* ---- Vertical timeline rail (left column) ---- */
.cjo-rail {
  position: relative;
  width: 80px;
  align-self: stretch;
  /* the rail fills the height of the cards column via grid alignment */
}
.cjo-rail__line {
  position: absolute;
  left: 38.5px;        /* 40px center - 1.5px (half of 3px line) */
  top: 0px;            /* JS sets this to badge-1 center */
  bottom: 0px;         /* JS sets this to badge-4 center */
  width: 3px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 3px;
  overflow: hidden;
}
.cjo-rail__fill {
  position: absolute;
  inset: 0 0 auto 0;
  height: 0%;
  background: linear-gradient(
    180deg,
    #F5C842 0%,
    #F09030 33.33%,
    #E05E20 66.66%,
    #C42A10 100%
  );
  transition: height 400ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
  border-radius: 2px;
  transform-origin: top;
}

.cjo-rail__node {
  position: absolute;
  left: 20px;          /* 40 center - 20 (half of 40) */
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--bg);
  border: 2.5px solid rgba(255, 255, 255, 0.28);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  /* Filter glow fades in over 180ms when is-glowing is added (Phase D
     of activation). Deactivation overrides this via .is-deactivating. */
  filter: none;
  transition: filter 180ms linear;
}
/* Deactivation transition (R1) — quieter than activation: only color +
   glow change, scale stays at 1. Cubic-bezier(0.4, 0.0, 0.6, 1.0) over
   350ms across bg, border, glow, and number text color. */
.cjo-rail__node.is-deactivating {
  transition:
    background-color 350ms cubic-bezier(0.4, 0.0, 0.6, 1.0),
    border-color     350ms cubic-bezier(0.4, 0.0, 0.6, 1.0),
    filter           350ms cubic-bezier(0.4, 0.0, 0.6, 1.0);
}
.cjo-rail__node.is-deactivating .cjo-rail__num {
  transition: color 350ms cubic-bezier(0.4, 0.0, 0.6, 1.0);
}
.cjo-rail__num {
  position: relative;
  z-index: 2;
  font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
  font-size: 14px;
  color: rgba(255, 255, 255, 0.6);
  line-height: 1;
  letter-spacing: 0;
  font-weight: 700;
  transition: color 100ms linear;
}
/* Pulse ring overlay for the power-up burst */
.cjo-rail__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--stage-accent);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
}

/* Activated resting state (after the 700ms power-up completes) */
.cjo-rail__node.is-active {
  background: var(--stage-accent);
  border-color: var(--stage-accent);
}
.cjo-rail__node.is-active .cjo-rail__num {
  color: #fff;
}
/* Soft persistent glow as the resting afterimage. */
.cjo-rail__node.is-glowing {
  filter: drop-shadow(0 0 8px color-mix(in oklab, var(--stage-accent) 60%, transparent));
}

/* === Mario-bros power-up choreography === */
/* Phase A (0–80ms anticipation) → Phase B (80–320ms overshoot + color snap + ring)
   → Phase C (320–520ms settle bounce) → Phase D (520–700ms glow fade-in, handled by .is-glowing) */
@keyframes cjo-badge-pop {
  0%     { transform: scale(1); }
  11.43% { transform: scale(0.88);  animation-timing-function: cubic-bezier(0.34, 1.8, 0.64, 1.0); } /* 80ms / 700ms = 11.43% */
  45.71% { transform: scale(1.5);   animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1.0); } /* 320ms */
  74.29% { transform: scale(1); } /* 520ms */
  100%   { transform: scale(1); }
}
@keyframes cjo-badge-color-snap {
  0%     { background-color: var(--bg);          border-color: rgba(255, 255, 255, 0.28); }
  11.43% { background-color: var(--bg);          border-color: rgba(255, 255, 255, 0.28); }
  /* color snap completes at end of first 100ms of phase B → 180ms total → 25.71% */
  25.71% { background-color: var(--stage-accent); border-color: var(--stage-accent); }
  100%   { background-color: var(--stage-accent); border-color: var(--stage-accent); }
}
@keyframes cjo-badge-num-color {
  0%     { color: rgba(255, 255, 255, 0.6); }
  11.43% { color: rgba(255, 255, 255, 0.6); }
  25.71% { color: #fff; }
  100%   { color: #fff; }
}
@keyframes cjo-badge-ring {
  0%     { opacity: 0; transform: scale(1); }
  11.43% { opacity: 0; transform: scale(1); animation-timing-function: cubic-bezier(0.2, 0.0, 0.4, 1.0); }
  11.44% { opacity: 0.8; transform: scale(1); animation-timing-function: cubic-bezier(0.2, 0.0, 0.4, 1.0); }
  45.71% { opacity: 0; transform: scale(2.25); } /* 90 / 40 = 2.25 */
  100%   { opacity: 0; transform: scale(2.25); }
}

.cjo-rail__node.is-popping {
  animation: cjo-badge-pop 700ms forwards;
}
.cjo-rail__node.is-popping {
  animation:
    cjo-badge-pop 700ms forwards,
    cjo-badge-color-snap 700ms forwards;
}
.cjo-rail__node.is-popping .cjo-rail__num {
  animation: cjo-badge-num-color 700ms forwards;
}
.cjo-rail__node.is-popping .cjo-rail__ring {
  animation: cjo-badge-ring 700ms forwards;
}

@media (prefers-reduced-motion: reduce) {
  .cjo-rail__node.is-popping,
  .cjo-rail__node.is-popping .cjo-rail__num,
  .cjo-rail__node.is-popping .cjo-rail__ring {
    animation: none;
  }
}

/* ---- Cards column ---- */
.cjo-cards {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.cjo-card {
  position: relative;
  /* Matches .friction-card glass-tinted background for visual consistency
     across Services and Friction sections. */
  background: oklch(0.18 0.07 290 / 0.6);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 20px;
  padding: 40px;
  overflow: hidden;
  /* Slower exit (350ms) than entry (280ms via :hover) so the card lingers
     in its illuminated state momentarily before returning. */
  transition: background    400ms ease,
              border-color  350ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
              box-shadow    350ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.cjo-card__bg {
  position: absolute;
  top: -50px;
  right: -50px;
  color: var(--stage-accent);
  pointer-events: none;
  z-index: 0;
  /* Soft accent-color glow on the whole illustration (Refinement 3) */
  filter: drop-shadow(0 0 5px color-mix(in oklab, var(--stage-accent) 50%, transparent));
}
.cjo-card__bg svg { display: block; overflow: visible; }

/* ====================================================================
   Refinement 2 — cursor-following spotlight on card hover
   --spotlight-x / --spotlight-y are written by a pointermove listener in
   looptimize.js. The spotlight pseudo-element sits at z-index 0 (above
   the card's solid #15102A background, below all content which is
   already at z-index 1).
   ==================================================================== */
.cjo-card {
  --spotlight-x: 50%;
  --spotlight-y: 50%;
}
.cjo-card[data-stage="0"] {
  --spot-c1: rgba(245, 200, 66, 0.22);
  --spot-c2: rgba(245, 200, 66, 0.08);
}
.cjo-card[data-stage="1"] {
  --spot-c1: rgba(240, 144, 48, 0.22);
  --spot-c2: rgba(240, 144, 48, 0.08);
}
.cjo-card[data-stage="2"] {
  --spot-c1: rgba(224, 94, 32, 0.22);
  --spot-c2: rgba(224, 94, 32, 0.08);
}
.cjo-card[data-stage="3"] {
  --spot-c1: rgba(196, 42, 16, 0.22);
  --spot-c2: rgba(196, 42, 16, 0.08);
}
.cjo-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    circle 280px at var(--spotlight-x) var(--spotlight-y),
    var(--spot-c1) 0%,
    var(--spot-c2) 35%,
    transparent 70%
  );
  opacity: 0;
  pointer-events: none;
  z-index: 0;
  transition: opacity 350ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
@media (hover: hover) {
  .cjo-card:hover {
    /* Matches .friction-card hover-tint; keeps glass consistency across sections. */
    background: oklch(0.24 0.10 295 / 0.7);
    /* Border picks up a soft stage-accent tint, but kept low-saturation so the
       overall card brightness matches the friction-card hover. The previous
       version stacked a 1px inset ring + a 32px outer bloom + a 60%-accent
       border, which together read as "the whole card got brighter" even
       though the bg color was identical. We drop both the inset ring and
       the outer bloom — keeping just the subtle border tint. */
    border-color: color-mix(in oklab, var(--stage-accent) 32%, transparent);
    transition:
      background   280ms ease,
      border-color 280ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
  }
  .cjo-card:hover::before {
    opacity: 1;
    transition: opacity 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
  }
}

/* ====================================================================
   Card content styling (restored: head, bars, title, subtitle, desc,
   service areas, pills with gradient hover from earlier refinements)
   ==================================================================== */

/* ---- Head row + bars (Refinement 2 of round 4 relocated bars below label) ---- */
.cjo-card__head {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}
.cjo-card__pos {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.5);
}
.cjo-card__bars {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 8px;
}
.cjo-card__bars i {
  display: block;
  width: 24px;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.12);
}
.cjo-card__bars i.is-on {
  background: var(--stage-accent);
}

/* ---- Title, subtitle, description ---- */
.cjo-card__title {
  position: relative;
  z-index: 1;
  margin: 24px 0 0;
  font-size: 32px;
  line-height: 1.15;
  font-weight: 700;
  color: #fff;
  letter-spacing: -0.01em;
}
.cjo-card__subtitle {
  position: relative;
  z-index: 1;
  margin: 10px 0 0;
  font-size: 17px;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.72);
}
.cjo-card__desc {
  position: relative;
  z-index: 1;
  margin: 28px 0 0;
  font-size: 15px;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.78);
  max-width: 64ch;
}

/* ---- Blocks + small-caps labels ---- */
.cjo-card__block {
  position: relative;
  z-index: 1;
  margin-top: 32px;
}
.cjo-card__label {
  margin: 0 0 14px;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.45);
}

/* ---- Service areas list with stage-accent bullet dots ---- */
.cjo-card__areas {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cjo-card__areas li {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 15px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.95);
}
.cjo-card__areas li::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--stage-accent);
  flex-shrink: 0;
}

/* ---- Capability pills: transparent dark base + rotating gradient border on hover ---- */
.cjo-card__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 16px;
}

/* Stage-specific gradient stops for the hover border */
.cjo-card[data-stage="0"] { --pill-grad-a: #F5C842; --pill-grad-b: #FFE680; }
.cjo-card[data-stage="1"] { --pill-grad-a: #F09030; --pill-grad-b: #F5B860; }
.cjo-card[data-stage="2"] { --pill-grad-a: #E05E20; --pill-grad-b: #FF8B50; }
.cjo-card[data-stage="3"] { --pill-grad-a: #C42A10; --pill-grad-b: #FF5840; }

@property --cjo-pill-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 135deg;
}

.cjo-pill {
  position: relative;
  display: inline-block;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.85);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  line-height: 1.3;
  letter-spacing: 0;
  cursor: default;
  transition: color 180ms cubic-bezier(0.4, 0.0, 0.2, 1.0),
              border-color 180ms cubic-bezier(0.4, 0.0, 0.2, 1.0),
              box-shadow 180ms cubic-bezier(0.4, 0.0, 0.2, 1.0);
  --cjo-pill-angle: 135deg;
}

/* Gradient border layer — sits as a ring around the pill, hidden by default */
.cjo-pill::before {
  content: "";
  position: absolute;
  inset: -1.5px;
  border-radius: 999px;
  padding: 1.5px;
  background: linear-gradient(var(--cjo-pill-angle), var(--pill-grad-a, #fff), var(--pill-grad-b, #fff));
  -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;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms cubic-bezier(0.4, 0.0, 0.2, 1.0);
}

@keyframes cjo-pill-spin {
  to { --cjo-pill-angle: 495deg; }
}

@media (hover: hover) {
  .cjo-pill:hover {
    border-color: transparent;
    color: #fff;
    box-shadow: 0 0 8px 0 color-mix(in oklab, var(--pill-grad-a) 40%, transparent);
    animation: cjo-pill-spin 3s linear infinite;
  }
  .cjo-pill:hover::before {
    opacity: 1;
  }
}

/* ====================================================================
   Refinement 3 (latest) — animated ambient illustrations, cosmic style
   Each illustration uses var(--stage-accent) via currentColor on .cjo-card__bg
   and inherits a 5px accent-color glow from the .cjo-card__bg drop-shadow.
   ==================================================================== */

/* ---- STAGE 1 — Orbital Observatory ---- */
.cjo-art--observatory .cjo-art__ring {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.2;
  stroke-opacity: 0.3;
}
.cjo-art--observatory .cjo-art__scan {
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
}
.cjo-art--observatory .cjo-art__sphere {
  /* fill is the radial gradient via url(#cjo-sphere-grad) */
}
.cjo-art--observatory .cjo-art__sat {
  fill: currentColor;
  fill-opacity: 1;
}

/* ---- STAGE 2 — Magnifying lens, 5s loop ----
   Phase A 0–1.0s  = 0–20%  : hold (lens visible, dots inside dim, lines hidden)
   Phase B 1.0–2.2s = 20–44% : checkmark lines draw, connected dots pulse
   Phase C 2.2–3.2s = 44–64% : whole lens "recognition" pulse
   Phase D 3.2–4.5s = 64–90% : hold the formed pattern
   Phase E 4.5–5.0s = 90–100%: lines fade, dots dim back, reset */
/* Selector covers both the legacy <circle> form and the current <use>
   twinkle-star form (each sky dot is now wrapped in <g><use/></g>). */
.cjo-art--lens .cjo-art__outside circle,
.cjo-art--lens .cjo-art__outside use {
  fill-opacity: 0.25;
}
.cjo-art--lens .cjo-art__handle {
  stroke: currentColor;
  stroke-width: 6;
  stroke-linecap: round;
  stroke-opacity: 0.4;
  animation: cjo-lens-pulse 5s linear infinite;
}
.cjo-art--lens .cjo-art__lens-ring {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.5;
  stroke-opacity: 0.4;
  animation: cjo-lens-pulse 5s linear infinite;
}
/* Whole-lens "ding" — opacity +30% during phase C (44–64%). */
@keyframes cjo-lens-pulse {
  0%, 44% { stroke-opacity: 0.4; }
  54%     { stroke-opacity: 0.7; }
  64%     { stroke-opacity: 0.4; }
  100%    { stroke-opacity: 0.4; }
}

/* Checkmark lines: stagger via --i = 0/1/2 (line i begins at 20% + i*4%). */
.cjo-art--lens .cjo-art__check-line {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  stroke-opacity: 0;
  animation: cjo-lens-line 5s linear infinite;
  animation-delay: calc(var(--i) * 200ms);
}
@keyframes cjo-lens-line {
  /* Phase A: hidden */
  0%, 20%  { stroke-dashoffset: 1; stroke-opacity: 0;   }
  /* Phase B: draw in over 400ms = 8% */
  28%      { stroke-dashoffset: 0; stroke-opacity: 0.5; }
  /* Phase C: pulse spike */
  44%      { stroke-dashoffset: 0; stroke-opacity: 0.5; }
  54%      { stroke-dashoffset: 0; stroke-opacity: 0.85;}
  64%      { stroke-dashoffset: 0; stroke-opacity: 0.5; }
  /* Phase D: hold */
  90%      { stroke-dashoffset: 0; stroke-opacity: 0.5; }
  /* Phase E: fade */
  100%     { stroke-dashoffset: 0; stroke-opacity: 0;   }
}

/* Check dots: pulse briefly as their incoming line completes (B), plus the
   whole-lens recognition spike during phase C. Each dot inherits an --i so
   its pulse aligns with its connecting line's completion. */
.cjo-art--lens .cjo-art__check-dot {
  fill-opacity: 0.6;
  transform-box: fill-box;
  transform-origin: center;
  animation: cjo-lens-dot 5s linear infinite;
  animation-delay: calc(var(--i) * 200ms);
}
@keyframes cjo-lens-dot {
  0%, 28%  { transform: scale(1);   fill-opacity: 0.6; }
  /* Brief 200ms pulse when this dot's line completes */
  31%      { transform: scale(1.3); fill-opacity: 1;   }
  34%      { transform: scale(1);   fill-opacity: 0.6; }
  /* Whole-lens recognition spike during phase C */
  44%      { transform: scale(1);   fill-opacity: 0.6; }
  54%      { transform: scale(1.1); fill-opacity: 0.9; }
  64%      { transform: scale(1);   fill-opacity: 0.6; }
  /* Phase D hold, Phase E dim */
  90%      { transform: scale(1);   fill-opacity: 0.6; }
  100%     { transform: scale(1);   fill-opacity: 0.4; }
}

/* ---- STAGE 3 — A/B Testing Channels ----
   Two parallel channels run independently. The asymmetry between them tells
   the story: B is the winner.
     • Top channel A (control): sparse + slow + dim + tepid reactions
         spawn every 2.2s, travel 3.2s, particle opacity 0.45, flat outcome
     • Bottom channel B (winner): dense + fast + bright + vivid reactions
         spawn every 0.8s, travel 1.8s, particle opacity 0.90, arrow outcome */

/* Letter labels sit flush at the left tip — no enclosing circle. */
.cjo-art--ab .cjo-art__label-text {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  fill: currentColor;
  fill-opacity: 0.85;
  font-weight: 700;
  dominant-baseline: middle;
}

/* ---- Top (A) ticks: cycle 2.2s, peak 70% opacity, ~200ms flash ---- */
.cjo-art--ab .cjo-art__tick {
  stroke-opacity: 0.5;
  transform-box: fill-box;
  transform-origin: center;
}
.cjo-art--ab .cjo-art__ticks--top .cjo-art__tick {
  animation: cjo-ab-tick-top 2.2s linear infinite;
  animation-delay: var(--phase, 0s);
}
@keyframes cjo-ab-tick-top {
  0%      { stroke-opacity: 0.5; }
  5%      { stroke-opacity: 0.7; }
  14%     { stroke-opacity: 0.5; }
  100%    { stroke-opacity: 0.5; }
}

/* ---- Bottom (B) ticks: cycle 0.8s, peak 100% + brief scale pulse ---- */
.cjo-art--ab .cjo-art__ticks--bot .cjo-art__tick {
  animation: cjo-ab-tick-bot 0.8s linear infinite;
  animation-delay: var(--phase, 0s);
}
@keyframes cjo-ab-tick-bot {
  0%      { stroke-opacity: 0.5; transform: scale(1);    }
  5%      { stroke-opacity: 1;   transform: scale(1.15); }
  18%     { stroke-opacity: 0.5; transform: scale(1);    }
  100%    { stroke-opacity: 0.5; transform: scale(1);    }
}

/* ---- Outcomes ---- */
.cjo-art--ab .cjo-art__outcome {
  transform-box: fill-box;
  transform-origin: center;
}

/* Top (A) = FLAT — tepid: brightens 0.5 → 0.65 over 150ms then returns.
   Cycle = 2.2s (spawn interval). Arrival lands at (3.2 mod 2.2) = 1.0s
   → 45.5% phase. 150ms flash = 6.8% of cycle, so peak at 49% phase. */
.cjo-art--ab .cjo-art__outcome--flat rect {
  fill-opacity: 0.5;
  animation: cjo-ab-flat 2.2s linear infinite;
}
@keyframes cjo-ab-flat {
  0%, 45.5% { fill-opacity: 0.5;  }
  49%       { fill-opacity: 0.65; }
  52.3%     { fill-opacity: 0.5;  }
  100%      { fill-opacity: 0.5;  }
}

/* Bottom (B) = UP ARROW — vivid: scales 1 → 1.35 → 1 over 280ms with overshoot
   ease, plus a glow spike. Cycle = 0.8s. Arrival at (1.8 mod 0.8) = 0.2s
   → 25% phase. 280ms = 35% of cycle, so phase 25 → 60. */
.cjo-art--ab .cjo-art__outcome--win path {
  fill-opacity: 1;
}
.cjo-art--ab .cjo-art__outcome--win {
  animation:
    cjo-ab-win-scale 0.8s cubic-bezier(0.34, 1.56, 0.64, 1.0) infinite,
    cjo-ab-win-glow  0.8s linear infinite;
}
@keyframes cjo-ab-win-scale {
  0%, 25% { transform: scale(1);    }
  42.5%   { transform: scale(1.35); }
  60%     { transform: scale(1);    }
  100%    { transform: scale(1);    }
}
@keyframes cjo-ab-win-glow {
  0%, 25% { filter: drop-shadow(0 0 2px color-mix(in oklab, currentColor 60%, transparent)); }
  42.5%   { filter: drop-shadow(0 0 8px color-mix(in oklab, currentColor 95%, transparent)); }
  60%     { filter: drop-shadow(0 0 2px color-mix(in oklab, currentColor 60%, transparent)); }
  100%    { filter: drop-shadow(0 0 2px color-mix(in oklab, currentColor 60%, transparent)); }
}

/* ---- Particles ---- */
/* Top (A): 2 particles, cycle 4.4s, travel 3.2s = 72.7% of cycle, opacity 0.45. */
.cjo-art--ab .cjo-art__particle--top {
  offset-path: path("M 50 102 L 248 102");
  offset-distance: 0%;
  offset-rotate: 0deg;
  animation: cjo-ab-comet-top 4.4s linear infinite;
}
@keyframes cjo-ab-comet-top {
  0%     { offset-distance: 0%;   opacity: 0; }
  3%     { opacity: 0.45; }
  72.7%  { offset-distance: 100%; opacity: 0.45; }
  73%    { offset-distance: 100%; opacity: 0; }
  100%   { offset-distance: 100%; opacity: 0; }
}
/* Bottom (B): 3 particles, cycle 2.4s, travel 1.8s = 75% of cycle, opacity 0.90. */
.cjo-art--ab .cjo-art__particle--bot {
  offset-path: path("M 50 132 L 248 132");
  offset-distance: 0%;
  offset-rotate: 0deg;
  animation: cjo-ab-comet-bot 2.4s linear infinite;
}
@keyframes cjo-ab-comet-bot {
  0%     { offset-distance: 0%;   opacity: 0; }
  3%     { opacity: 0.9; }
  75%    { offset-distance: 100%; opacity: 0.9; }
  76%    { offset-distance: 100%; opacity: 0; }
  100%   { offset-distance: 100%; opacity: 0; }
}

/* ---- STAGE 4 — Propagating Network ---- */
/* 2.8s cycle. Phase boundaries (in cycle %):
   0 → 32.1% (0–0.9s): primary pulse travels center → secondary
   32.1 → 55%   (0.9–1.54s): secondary nodes twinkle (anticipation → bloom → afterglow)
   32.1 → 64.3% (0.9–1.8s):  sub-pulses travel secondary → tertiary
   64.3 → 87%   (1.8–2.44s): tertiary nodes twinkle (same envelope)
   the center carries a slow ambient breath so it always reads as a live star. */
.cjo-art--network .cjo-art__center {
  fill: currentColor;
  fill-opacity: 0.95;
  transform-box: fill-box;
  transform-origin: center;
  animation: cjo-net-center-twinkle 2.8s ease-in-out infinite;
}
.cjo-art--network .cjo-art__s,
.cjo-art--network .cjo-art__t {
  fill: currentColor;
  transform-box: fill-box;
  transform-origin: center;
}
.cjo-art--network .cjo-art__p,
.cjo-art--network .cjo-art__sp {
  fill: currentColor;
  offset-distance: 0%;
  opacity: 0;
}

.cjo-art--network .cjo-art__p  { animation: cjo-net-primary 2.8s linear infinite; }
.cjo-art--network .cjo-art__sp { animation: cjo-net-sub     2.8s linear infinite; }
.cjo-art--network .cjo-art__s  { animation: cjo-net-flash-s 2.8s linear infinite; }
.cjo-art--network .cjo-art__t  { animation: cjo-net-flash-t 2.8s linear infinite; }

@keyframes cjo-net-primary {
  0%      { offset-distance: 0%;   opacity: 0; }
  3%      { opacity: 1; }
  32.1%   { offset-distance: 100%; opacity: 1; }
  32.5%   { offset-distance: 100%; opacity: 0; }
  100%    { offset-distance: 100%; opacity: 0; }
}
@keyframes cjo-net-sub {
  0%, 32.1% { offset-distance: 0%; opacity: 0; }
  33%       { opacity: 1; }
  64.3%     { offset-distance: 100%; opacity: 1; }
  65%       { offset-distance: 100%; opacity: 0; }
  100%      { offset-distance: 100%; opacity: 0; }
}
/* Twinkle envelope, applied to secondaries when the primary pulse arrives.
   Animation principles:
     • anticipation: a tiny inhale dip just before the pulse lands
     • bloom: a back-out overshoot for a physical “got hit by light” arrival
     • afterglow: long ease-out decay back to baseline
   The per-keyframe animation-timing-function controls each segment’s feel
   so the scale never reads as a square step. */
@keyframes cjo-net-flash-s {
  0%, 28%   { transform: scale(1);    fill-opacity: 0.55; animation-timing-function: ease-in;  }
  30.5%     { transform: scale(0.9);  fill-opacity: 0.5;  animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }
  34%       { transform: scale(1.55); fill-opacity: 1;    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  55%       { transform: scale(1);    fill-opacity: 0.55; animation-timing-function: linear; }
  100%      { transform: scale(1);    fill-opacity: 0.55; }
}
@keyframes cjo-net-flash-t {
  0%, 60%   { transform: scale(1);    fill-opacity: 0.5; animation-timing-function: ease-in;  }
  62.5%     { transform: scale(0.9);  fill-opacity: 0.45; animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }
  66%       { transform: scale(1.55); fill-opacity: 1;    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  87%       { transform: scale(1);    fill-opacity: 0.5; animation-timing-function: linear; }
  100%      { transform: scale(1);    fill-opacity: 0.5; }
}
/* Slow ambient breath on the central star so it reads as a living source
   even between pulse cycles. Synced to the loop — dimmest at rest, peak
   right around the primary-pulse launch. */
@keyframes cjo-net-center-twinkle {
  0%, 100% { transform: scale(1);    fill-opacity: 0.85; }
  3%       { transform: scale(1.14); fill-opacity: 1; }
  35%      { transform: scale(1);    fill-opacity: 0.85; }
}

/* Reduced motion: hold static end-states for each illustration. */
@media (prefers-reduced-motion: reduce) {
  .cjo-art--lens .cjo-art__check-line,
  .cjo-art--lens .cjo-art__check-dot,
  .cjo-art--lens .cjo-art__handle,
  .cjo-art--lens .cjo-art__lens-ring,
  .cjo-art--ab .cjo-art__tick,
  .cjo-art--ab .cjo-art__outcome--win,
  .cjo-art--ab .cjo-art__outcome--flat rect,
  .cjo-art--ab .cjo-art__particle,
  .cjo-art--network .cjo-art__center,
  .cjo-art--network .cjo-art__p,
  .cjo-art--network .cjo-art__sp,
  .cjo-art--network .cjo-art__s,
  .cjo-art--network .cjo-art__t {
    animation: none;
  }
  .cjo-art--lens .cjo-art__check-line { stroke-dashoffset: 0; stroke-opacity: 0.5; }
  .cjo-art--ab .cjo-art__particle,
  .cjo-art--network .cjo-art__p,
  .cjo-art--network .cjo-art__sp { opacity: 0; }
}

/* ---- Mobile ---- */
@media (max-width: 767px) {
  .cjo-journey {
    grid-template-columns: 1fr;
  }
  .cjo-rail {
    display: none;
  }
  .cjo-cards {
    gap: 20px;
  }
  .cjo-card {
    padding: 24px;
    border-radius: 16px;
  }
  .cjo-card__title { font-size: 26px; }
  .cjo-card__bg { transform: scale(0.75); transform-origin: top right; }
}

/* ---------------------------------------------------------------
   Services CTA card.
   Background: contained instance of the hero's liquid shader, dimmed
   to ~65% so the card stays predominantly dark. The shader canvas is
   absolutely positioned to inset:0 and clipped by the card's
   border-radius + overflow:hidden.
   Border: gradient via mask-composite ring (same technique as the
   .cjo-pill hover state). Conic gradient runs clockwise — luminous
   violet→coral across the top, fading to near-invisible dark purple
   at the bottom; directional, not a uniform glow.
   --------------------------------------------------------------- */

/* Registered so opacity values can transition smoothly on hover.
   (Plain custom-property changes don't animate by default.) */
@property --cta-bd-a-top    { syntax: "<number>"; inherits: true; initial-value: 0.70; }
@property --cta-bd-a-coral  { syntax: "<number>"; inherits: true; initial-value: 0.70; }
@property --cta-bd-a-bottom { syntax: "<number>"; inherits: true; initial-value: 0.15; }
@property --cta-bd-a-bl     { syntax: "<number>"; inherits: true; initial-value: 0.45; }
/* Shader-edge brightness multiplier — bumped on hover. */
@property --cta-shader-boost { syntax: "<number>"; inherits: true; initial-value: 1; }

.services__cta {
  margin-top: 4rem;
  border-radius: 24px;
  /* Border lives on the ::after ring — leave the box border off. */
  border: none;
  /* Solid dark surface, slightly lighter than the page background so the
     card reads as a distinct surface without needing a gradient. The
     particle field on top supplies the atmosphere. */
  background: #0E0B1F;
  padding: 2.2rem clamp(1.5rem, 4vw, 2.8rem);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.5rem;
  position: relative; overflow: hidden; isolation: isolate;

  /* Border-stop alphas as variables so :hover can animate them. */
  --cta-bd-a-top: 0.70;
  --cta-bd-a-coral: 0.70;
  --cta-bd-a-bottom: 0.15;
  --cta-bd-a-bl: 0.45;
  --cta-shader-boost: 1;

  transition:
    --cta-bd-a-top    300ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
    --cta-bd-a-coral  300ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
    --cta-bd-a-bottom 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
    --cta-bd-a-bl     300ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
    /* Shader edge glow: ~300ms on the way in / 500ms on the way out
       comes from giving the property a 500ms transition and a per-hover
       override below. */
    --cta-shader-boost 500ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* Contained edge-glow shader. The CSS mask carves the center 60% of the
   card transparent and reveals the shader only in the outer ~20% band
   around the perimeter — so the dark solid surface dominates and the
   shader reads as a faint atmospheric glow at the edges. The card's
   border-radius + overflow:hidden clip the canvas; the brightness
   filter is bumped on hover via the registered --cta-shader-boost. */
.services__cta-shader {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
  background: transparent;
  /* Radial gradient mask: transparent through the inner core, opaque
     past ~70% so the outer band carries the glow. Card is wide and
     short — the 150%/180% ellipse stretches the visible band wider
     horizontally than vertically so the long top/bottom edges glow as
     much as the short left/right edges. */
  -webkit-mask: radial-gradient(
    ellipse 150% 180% at 50% 50%,
    rgba(0,0,0,0)   0%,
    rgba(0,0,0,0)   15%,
    rgba(0,0,0,1)   65%,
    rgba(0,0,0,1)   100%
  );
          mask: radial-gradient(
    ellipse 150% 180% at 50% 50%,
    rgba(0,0,0,0)   0%,
    rgba(0,0,0,0)   15%,
    rgba(0,0,0,1)   65%,
    rgba(0,0,0,1)   100%
  );
  filter: brightness(var(--cta-shader-boost));
}

/* Contained sparkle field. Uses the same 4-point twinkle visual as the
   Our Approach card (.ap-star + apTwinkle), restyled into the card's
   violet/coral palette and limited to a very slow vertical drift — no
   horizontal motion. Each sparkle is a positioned <span> generated by
   cta-particles.js; CSS handles the twinkle scale/opacity loop and the
   vertical drift loop on independent timings per element. */
.services__cta-particles {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 1;
  pointer-events: none;
  /* Faded in by JS over ~1.2s on load so the card doesn't flash on
     first paint; hover lifts on the bright anchor sparkles are handled
     by CSS via .is-hover on the parent card. */
  opacity: 0;
  transition: opacity 1.2s ease-out;
  overflow: hidden;
}

/* Individual sparkle wrapper. The drift animation translates Y between
   ±var(--drift-amp) over var(--drift-dur), randomized per sparkle.
   The inner element handles the twinkle (opacity + scale). Splitting
   it across two elements means the two animations don't fight on a
   single `transform`. */
.cta-sparkle {
  position: absolute;
  width: var(--size, 8px);
  height: var(--size, 8px);
  color: var(--tint, oklch(0.93 0.06 305));
  /* drift: slow vertical-only oscillation, 15–30s loop, randomized
     phase so sparkles never sync. */
  animation: ctaSparkleDrift var(--drift-dur, 22s) ease-in-out infinite;
  animation-delay: var(--drift-delay, 0s);
  will-change: transform;
}
.cta-sparkle__inner {
  display: block;
  width: 100%;
  height: 100%;
  /* twinkle: identical scale/opacity envelope to .ap-star (apTwinkle).
     Period randomized per sparkle. */
  animation: ctaSparkleTwinkle var(--tw, 3s) ease-in-out infinite;
  animation-delay: var(--tw-delay, 0s);
  transform-origin: center;
  will-change: transform, opacity;
}
.cta-sparkle__inner svg { display: block; width: 100%; height: 100%; fill: currentColor; }
.cta-sparkle__inner.is-mote {
  background: currentColor;
  border-radius: 50%;
}

@keyframes ctaSparkleTwinkle {
  0%, 100% { opacity: 0.20; transform: scale(0.6); }
  50%      { opacity: 1;    transform: scale(1); }
}
@keyframes ctaSparkleDrift {
  0%, 100% { transform: translateY(calc(var(--drift-amp, 12px) * -1)); }
  50%      { transform: translateY(var(--drift-amp, 12px)); }
}

/* Anchor sparkles brighten on card hover — keeps the same hover
   storytelling as the prior canvas version. Plays on the inner so it
   composes with the twinkle's scale on the same element via the color/
   filter properties, not transform. */
.services__cta-particles .cta-sparkle--anchor .cta-sparkle__inner {
  transition: filter 600ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
  filter: brightness(1) drop-shadow(0 0 0 transparent);
}
@media (hover: hover) {
  .services__cta:hover .cta-sparkle--anchor .cta-sparkle__inner {
    filter: brightness(1.6)
            drop-shadow(0 0 6px oklch(0.95 0.05 305 / 0.6));
    transition-duration: 400ms;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cta-sparkle,
  .cta-sparkle__inner { animation: none !important; }
  .cta-sparkle__inner { opacity: 0.7; transform: none; }
}

/* Gradient border ring. 1.5px stroke around the card, mask-composite
   carves the inside out so only the border remains. Conic gradient
   starts at the top-left (from 315deg) and sweeps clockwise:
     top-left  → deep violet  #6B3FA0  ( alpha ~0.70 )
     top-right → coral-pink   #C06080  ( alpha ~0.70 )
     bottom    → dark purple             ( alpha ~0.15 )
     bottom-left → back to violet, faded ( alpha ~0.45 )
   The opacity gradient gives the directional "light from above" read. */
.services__cta::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from 315deg at 50% 50%,
    rgba(107,  63, 160, var(--cta-bd-a-top))    0%,
    rgba(192,  96, 128, var(--cta-bd-a-coral))  25%,
    rgba( 40,  20,  60, var(--cta-bd-a-bottom)) 55%,
    rgba(107,  63, 160, var(--cta-bd-a-bl))     80%,
    rgba(107,  63, 160, var(--cta-bd-a-top))    100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
}

/* Card text + button sit above the shader+particles (z-index 0/1) and
   below the border ring (z-index 2). */
.services__cta-text { position: relative; z-index: 2; }
.services__cta > .btn { position: relative; z-index: 2; }
.services__cta h3 { font-size: var(--fs-h3); margin-top: 0.6rem; }

/* Hover state: brighten the border stops by ~30% and lift the edge
   shader by ~20%. Cubic-bezier matches the rest of the section. */
@media (hover: hover) {
  .services__cta:hover {
    --cta-bd-a-top: 0.91;
    --cta-bd-a-coral: 0.91;
    --cta-bd-a-bottom: 0.20;
    --cta-bd-a-bl: 0.59;
    --cta-shader-boost: 1.20;
  }
}

/* =========================================================
   Testimonials
   ========================================================= */

.testimonials { padding: 7rem 0; }
.testimonials__inner { display: grid; gap: 3rem; grid-template-columns: 1fr; }
@media (min-width: 1000px) { .testimonials__inner { grid-template-columns: 5fr 7fr; gap: 4rem; } }
.testimonials h2 { font-size: var(--fs-h2); margin-top: 1.2rem; }
.testimonials__body { margin-top: 1.4rem; color: var(--fg-muted); font-size: var(--fs-body); }

.testimonials__tabs {
  margin-top: 2.2rem; display: flex; flex-direction: column; gap: 0.25rem;
}
.t-tab {
  display: flex; align-items: center; justify-content: space-between;
  text-align: left; padding: 0.9rem 0 0.9rem 1rem;
  border-left: 2px solid var(--border);
  color: var(--fg-muted);
  transition: all 0.25s;
}
.t-tab:hover { color: var(--fg); border-left-color: oklch(0.70 0.25 340 / 0.4); }
.t-tab.is-active { color: var(--fg); border-left-color: var(--signal); }
.t-tab__lhs { display: flex; align-items: center; gap: 0.9rem; }
.t-tab__avatar {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--bg-elev);
  border: 2px solid var(--border);
  overflow: hidden;
  flex-shrink: 0;
  transition: border-color 0.25s, box-shadow 0.25s;
}
.t-tab__avatar img { width: 100%; height: 100%; object-fit: cover; }
.t-tab.is-active .t-tab__avatar { border-color: var(--signal); box-shadow: 0 0 16px oklch(0.70 0.25 340 / 0.4); }
.t-tab__name { font-weight: 700; }
.t-tab__role { font-size: 0.78rem; color: var(--fg-dim); }
.t-tab__num { font-size: 0.75rem; color: var(--fg-dim); font-variant-numeric: tabular-nums; }

/* ===== Editorial portrait card ===== */
.testimonials__quote-card {
  position: relative;
  display: grid;
  grid-template-columns: 35% 1fr;
  min-height: 680px;
  background: #15102A;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);
}

/* Gradient border ring — left edge brightest, right fades to ZERO
   (anything above 0% alpha on the right shows as a faint stripe against
   the card shadow, which reads as misplaced light). Same mask-composite
   technique as .services__cta::after. */
.testimonials__quote-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from 225deg at 50% 50%,
    rgba(107,  63, 160, 0.70)   0%,    /* bottom-left — strong violet */
    rgba(107,  63, 160, 0.60)  12%,    /* left edge — brightest */
    rgba(192,  96, 128, 0.45)  28%,    /* top-left — coral */
    rgba(192,  96, 128, 0.00)  45%,    /* top — faded out */
    rgba(192,  96, 128, 0.00)  72%,    /* right + bottom-right — zero */
    rgba(107,  63, 160, 0.00)  85%,    /* bottom-right — still zero */
    rgba(107,  63, 160, 0.45)  95%,    /* bottom-left — returning violet */
    rgba(107,  63, 160, 0.70) 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 3;
}

.t-card__portrait {
  position: relative;
  align-self: stretch;
  min-height: 100%;
}
.t-card__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 25%;
  display: block;
  transform-origin: center center;
  will-change: opacity, transform;
  /* Fade the photo's alpha directly into the card bg — no overlay element,
     so the transition is a single smooth interpolation with no visible seam.
     Photo is solid for the first 65% of its zone, then tapers to transparent
     at the right edge where the content zone takes over. */
  -webkit-mask-image: linear-gradient(to right, #000 0%, #000 65%, transparent 100%);
          mask-image: linear-gradient(to right, #000 0%, #000 65%, transparent 100%);
  transition:
    opacity 380ms cubic-bezier(0.16, 1, 0.3, 1),
    transform 520ms cubic-bezier(0.16, 1, 0.3, 1);
}

.t-card__content {
  padding: 48px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: relative;
  z-index: 2;
  will-change: opacity, transform;
  transition:
    opacity 360ms cubic-bezier(0.16, 1, 0.3, 1),
    transform 480ms cubic-bezier(0.16, 1, 0.3, 1);
}

.t-card__logo-wrap {
  height: 44px;
  display: flex;
  align-items: center;
}
.t-card__logo {
  display: block;
  max-height: 100%;
  width: auto;
  height: auto;
  max-width: 180px;
  object-fit: contain;
}
/* BOOM! BEAUTY logo is black on transparent — invert to white on the dark card. */
.t-card__logo-wrap[data-logo-style="boom"] .t-card__logo {
  filter: invert(1) brightness(1.05);
  max-height: 36px;
  max-width: 160px;
}
/* Fleischrebellen logo is a colored square; give it a touch of breathing room. */
.t-card__logo-wrap[data-logo-style="fleisch"] .t-card__logo {
  max-height: 44px;
  width: 44px;
  border-radius: 4px;
}
/* Opencreek logo is a long horizontal orange mark — give it more width. */
.t-card__logo-wrap[data-logo-style="opencreek"] .t-card__logo {
  max-height: 30px;
  max-width: 170px;
}

.t-card__company {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #E040A0;
  margin: 0;
}

.t-card__quote {
  margin: 24px 0 0;
  font-size: 18px;
  line-height: 1.55;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.92);
  font-style: normal;
  text-wrap: pretty;
}

.t-card__rule {
  margin: 32px 0 0;
  border: 0;
  height: 1px;
  width: 40px;
  background: rgba(255, 255, 255, 0.25);
}

.t-card__byline {
  margin: 20px 0 0;
}
.t-card__name {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: #ffffff;
  line-height: 1.3;
}
.t-card__role {
  margin: 2px 0 0;
  font-size: 14px;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.70);
  line-height: 1.4;
}

/* Two-stage tab-swap choreography:
   .is-leaving — snappy exit (180ms, in-expo). Photo zooms in subtly
     as it fades; content drifts up.
   .is-entering — momentarily holds the new content in its "pre-arrival"
     state (photo zoomed out, content drifted down) with no transition,
     so the immediately-following class removal triggers a smooth
     out-expo landing back to rest. */
.testimonials__quote-card.is-leaving .t-card__photo {
  opacity: 0;
  transform: scale(1.035);
  transition:
    opacity 180ms cubic-bezier(0.7, 0, 0.84, 0),
    transform 220ms cubic-bezier(0.7, 0, 0.84, 0);
}
.testimonials__quote-card.is-leaving .t-card__content {
  opacity: 0;
  transform: translateY(-6px);
  transition:
    opacity 180ms cubic-bezier(0.7, 0, 0.84, 0),
    transform 220ms cubic-bezier(0.7, 0, 0.84, 0);
}
.testimonials__quote-card.is-entering .t-card__photo {
  opacity: 0;
  transform: scale(0.965);
  transition: none;
}
.testimonials__quote-card.is-entering .t-card__content {
  opacity: 0;
  transform: translateY(10px);
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .t-card__photo,
  .t-card__content,
  .testimonials__quote-card.is-leaving .t-card__photo,
  .testimonials__quote-card.is-leaving .t-card__content,
  .testimonials__quote-card.is-entering .t-card__photo,
  .testimonials__quote-card.is-entering .t-card__content {
    transition: opacity 120ms linear;
    transform: none;
  }
}

/* Mobile-only chip pager (visible <768px). Three small circular avatar
   buttons that proxy to the desktop tab logic so the selector travels
   with the card instead of scrolling off-screen. */
.t-card__pager { display: none; }
.t-card__pager-chip {
  -webkit-appearance: none; appearance: none;
  width: 42px; height: 42px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.04);
  overflow: hidden;
  cursor: pointer;
  opacity: 0.6;
  transition:
    opacity 0.3s ease,
    border-color 0.3s ease,
    transform 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 0.3s ease;
}
.t-card__pager-chip img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: 50%;
}
.t-card__pager-chip:hover { opacity: 0.85; }
.t-card__pager-chip.is-active {
  opacity: 1;
  border-color: #E040A0;
  box-shadow: 0 0 16px rgba(224, 64, 160, 0.45);
  transform: scale(1.08);
}
.t-card__pager-chip:focus-visible {
  outline: 2px solid #E040A0;
  outline-offset: 2px;
}

@media (max-width: 767px) {
  /* Hide the desktop tab strip on mobile — chips take over. */
  .testimonials__tabs { display: none; }

  .testimonials__quote-card {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
    min-height: 0;
  }
  /* Sticky chip pager: sibling of the card so it can escape the card's
     overflow:hidden and pin to the viewport top while the card scrolls
     past behind it. top:60px clears the fixed site header. */
  .t-card__pager {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 14px;
    padding: 12px 16px;
    margin-bottom: 14px;
    border-radius: 999px;
    background: rgba(21, 16, 42, 0.88);
    backdrop-filter: blur(6px) saturate(115%);
    -webkit-backdrop-filter: blur(6px) saturate(115%);
    border: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
    position: sticky;
    top: 60px;
    z-index: 5;
  }
  .t-card__portrait {
    aspect-ratio: 16 / 9;
    min-height: 0;
  }
  .t-card__photo {
    /* On mobile the portrait sits above the content, so fade downward instead. */
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 65%, transparent 100%);
            mask-image: linear-gradient(to bottom, #000 0%, #000 65%, transparent 100%);
  }
  .t-card__content { padding: 28px; }
}

/* =========================================================
   Contact
   ========================================================= */

.contact { padding: 8rem 0 6rem; position: relative; overflow: hidden; isolation: isolate; }
.contact > .container { position: relative; z-index: 1; }
.contact__aurora {
  position: absolute; inset: 0; top: 0; left: 0;
  width: 100%; height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
  opacity: 0.55;
  /* Feather the top edge so the ribbons fade in over the first ~18% of the
     section instead of cutting hard at the boundary with the section above. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 18%, #000 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 18%, #000 100%);
}
.contact__inner { display: grid; gap: 3rem; grid-template-columns: 1fr; }
@media (min-width: 1000px) { .contact__inner { grid-template-columns: 5fr 7fr; gap: 5rem; } }
.contact h2 { font-size: var(--fs-h2); margin-top: 1.2rem; }
.contact__body { margin-top: 1.4rem; color: var(--fg-muted); font-size: var(--fs-body); line-height: 1.55; }
.contact__bullets { margin-top: 2.2rem; display: flex; flex-direction: column; gap: 0.9rem; }
.contact__bullets li {
  list-style: none; display: flex; align-items: center; gap: 0.8rem;
  font-size: 0.95rem;
}
.contact__bullets li::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--signal); box-shadow: 0 0 10px var(--signal-glow);
}

.contact__form {
  position: relative; overflow: hidden;
  border-radius: 24px;
  border: 1px solid var(--border);
  background: linear-gradient(135deg, oklch(0.20 0.08 290 / 0.55), oklch(0.17 0.06 280 / 0.45));
  backdrop-filter: blur(8px);
  padding: clamp(1.5rem, 4vw, 2.5rem);
}
.contact__form::before {
  content: ""; position: absolute; left: -120px; top: -120px;
  width: 350px; height: 350px; border-radius: 50%;
  background: radial-gradient(circle, oklch(0.55 0.22 305 / 0.25), transparent 70%);
  filter: blur(20px); pointer-events: none;
}
.contact__fields { display: grid; gap: 1rem; grid-template-columns: 1fr; position: relative; }
@media (min-width: 600px) { .contact__fields { grid-template-columns: 1fr 1fr; } }
.field { display: flex; flex-direction: column; gap: 0.5rem; }
.field--full { grid-column: 1 / -1; }
.field label {
  font-size: 0.66rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--fg-muted);
  display: flex; justify-content: space-between;
}
.field input, .field textarea {
  width: 100%;
  background: oklch(0.13 0.06 285 / 0.6);
  border: 1px solid var(--border-strong);
  color: var(--fg);
  border-radius: 12px;
  padding: 0.85rem 1rem;
  font: inherit;
  font-size: 0.95rem;
  outline: none;
  transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
.field input:focus, .field textarea:focus {
  border-color: var(--signal);
  box-shadow: 0 0 0 4px oklch(0.70 0.25 340 / 0.15);
}
.field textarea { min-height: 130px; resize: vertical; }
.field--hp { display: none; }
.cf-turnstile { margin-top: 1.2rem; }
.contact__form .btn { margin-top: 1.6rem; }
.contact__error {
  margin-top: 1rem; font-size: 0.875rem;
  color: oklch(0.75 0.18 25);
  line-height: 1.5;
}

/* =========================================================
   Footer
   ========================================================= */

.footer { padding: 4rem 0 3rem; border-top: 1px solid var(--border); }
.footer__inner {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 1.5rem;
  font-size: 0.9rem; color: var(--fg-muted);
}
.footer .logo { color: var(--fg); }
.footer__links { display: flex; gap: 1.6rem; }
/* Footer links mirror the .nav link choreography from the header:
   small upward "pop" on hover with overshoot bezier, soft violet
   text-shadow glow, a center-out underline, and a generous hit-area
   guard pseudo-element so the cursor stays inside as the link lifts
   (prevents the hover-flicker the bare style had). */
.footer__links a {
  position: relative;
  display: inline-block;
  color: var(--fg-muted);
  transition:
    color 0.25s ease,
    transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1.00),
    text-shadow 0.25s ease;
  will-change: transform;
}
.footer__links a::before {
  content: "";
  position: absolute;
  inset: -8px -6px -10px -6px;
  pointer-events: auto;
  z-index: 0;
}
.footer__links a::after {
  content: "";
  position: absolute;
  left: 50%;
  right: 50%;
  bottom: -6px;
  height: 1px;
  background: linear-gradient(90deg,
    transparent,
    oklch(0.86 0.16 320 / 0.9),
    transparent);
  opacity: 0;
  pointer-events: none;
  transition:
    left 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    right 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.2s ease;
}
.footer__links a:hover,
.footer__links a:focus-visible {
  color: var(--fg);
  transform: translateY(-2px);
  text-shadow: 0 6px 18px oklch(0.72 0.18 320 / 0.55);
}
.footer__links a:hover::after,
.footer__links a:focus-visible::after {
  left: 0;
  right: 0;
  opacity: 1;
}

/* =========================================================
   Reduced motion
   ========================================================= */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  html { scroll-behavior: auto; }
}
