/* =====================================================================
   LeadsNimble How It Works — "Live in minutes, not weeks": a glowing hub
   dead-centre with three step cards orbiting it (one top, two bottom) and
   curved connector wires drawn from the hub to each.

   Ported 1:1 from leadsnimble_html:
     src/html/partials/how-it-works.html
     src/css/components/how-it-works.css   (.spokes stage, hub, wires, cards)
     src/css/base/1-reset.css              (h2/h3 face, weight, tracking, leading)
     src/css/base/2-layout.css             (.wrap, .section, .section__head, .eyebrow)
     src/css/base/3-utilities.css          (.reveal stagger, reduced motion)
     src/css/base/4-responsive.css         (section padding + head gap ramps)
     src/css/themes/aurora-nightfall/tokens.css

   The theme's design tokens are resolved to literal values and re-scoped under
   --lnhw-*, so the widget renders identically regardless of the active WP theme
   and never leaks variables into the rest of the page. LIGHT is the default
   mode (matching the reference build's mode.txt and the rest of the family);
   [data-mode="dark"] on <html> flips the palette.

   THE STAGE IS A COORDINATE SYSTEM, NOT A GRID. The reference lays the three
   cards out absolutely on a fixed 920x600 box so the SVG wires can be authored
   against predictable pixel positions. That is preserved here, but the three
   hard-coded rules (.spoke--1/2/3) are replaced by ONE rule driven by two
   per-card custom properties written server side:

     --lnhw-f   the card's horizontal fraction in its row (0 = flush left,
                1 = flush right, .5 = centred). left is then
                calc(f * (100% - card-width)), which is EXACTLY what the
                reference's `left:0` / `right:0` / `left:50%+translateX(-50%)`
                resolve to at every stage width — including when the stage is
                narrower than 920px and the cards stay edge-anchored.
     row class  .lnew-how__spoke--r1 / --r2 pick the row's `top`.

   That is what lets the widget take any number of steps (they split into a top
   and a bottom row) while the default three land on the reference's pixels.

   THE CARD SLAB IS --bg-soft, NOT the glass surface. In the reference,
   aurora's overrides.css re-skins .feature/.pain__card/.step/... as glass, but
   .spoke is deliberately NOT in that list — it keeps its own opaque
   `linear-gradient(160deg, ...) , var(--bg-soft)` fill. Opaque is load-bearing:
   the wires are drawn BEHIND the cards (z-index 0 vs 2) and a translucent
   surface would let the line show through. In light mode that makes the card
   (#cfd7ee) very slightly deeper than the section (#dde2f4) — that IS the
   reference, and both are controls if you want them to match.

   STACKING IS JS-DRIVEN (lnew-how.js), measured on the SECTION's own width
   rather than the viewport's, so the widget behaves correctly inside a narrow
   Elementor column too. The @media block at the bottom is only the no-JS
   fallback and is scoped away the moment the script boots.
   ===================================================================== */

/* Local reset. Wrapped in :where() so it carries ZERO specificity — a bare
   `.lnew-how p` would be (0,1,1) and would outrank every (0,1,0) component
   class below it. See [[hero-widget-feature]] / [[nav-widget-feature]] for the
   traps this avoids. Do not unwrap. */
.lnew-how, .lnew-how *, .lnew-how *::before, .lnew-how *::after { box-sizing: border-box; }
:where(.lnew-how) :where(a) { color: inherit; text-decoration: none; }
:where(.lnew-how) :where(p) { margin: 0; }
:where(.lnew-how) :where(article) { margin: 0; }
:where(.lnew-how) :where(svg) { display: block; }
:where(.lnew-how) :where(img) { max-width: 100%; display: block; }
:where(.lnew-how) :where(h1, h2, h3, h4) { margin: 0; }

.lnew-how {
  /* ---- motion / geometry ---- */
  --lnhw-ease: cubic-bezier(.22,1,.36,1);
  --lnhw-rv-dist: 70px;
  --lnhw-rv-dur: 1.2s;
  --lnhw-rv-step: 70ms;
  --lnhw-maxw: 1360px;
  --lnhw-gutter: 28px;
  --lnhw-title-size: clamp(31px, 3.6vw, 47px);
  --lnhw-head-gap: 56px;

  /* ---- the stage ---- */
  --lnhw-stage-w: 920px;
  --lnhw-stage-h: 600px;
  --lnhw-stage-top: 44px;
  --lnhw-stage-top-stacked: 30px;
  --lnhw-stack-gap: 18px;
  --lnhw-row-1: 0px;              /* top-row cards */
  --lnhw-row-2: 405px;            /* bottom-row cards */
  --lnhw-card-w: 320px;
  --lnhw-card-w-stacked: 440px;

  /* ---- the hub ---- */
  --lnhw-hub-size: 132px;
  --lnhw-hub-glyph: 50px;
  --lnhw-halo-spread: 58px;
  --lnhw-halo-stop: 62%;
  --lnhw-halo-blur: 4px;
  --lnhw-pulse-dur: 5s;
  --lnhw-pulse-scale: 1.08;
  --lnhw-hub-gap-stacked: 6px;

  /* ---- the wires ---- */
  --lnhw-wire-w: 2px;
  --lnhw-wire-glow-blur: 5px;

  /* ---- the cards ---- */
  --lnhw-card-pad-y: 22px;
  --lnhw-card-pad-x: 24px;
  --lnhw-card-radius: 24px;
  --lnhw-card-border-w: 1px;
  --lnhw-card-lift: -4px;
  --lnhw-n-size: 34px;
  --lnhw-n-font: 15px;
  --lnhw-n-gap: 14px;
  --lnhw-t-size: 18px;
  --lnhw-t-gap: 8px;
  --lnhw-d-size: 13.5px;

  /* ---- type ---- */
  --lnhw-font-sans: 'DM Sans', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --lnhw-font-head: 'Parkinsans', 'DM Sans', sans-serif;

  /* ---- palette · LIGHT (the site's default mode) ---- */
  --lnhw-bg: #dde2f4;             /* --bg, i.e. a plain .section */
  --lnhw-ink: #12122a;
  --lnhw-body: #454a63;
  --lnhw-muted: #6b7089;

  --lnhw-eyebrow-bg: #eef0ff;
  --lnhw-eyebrow-ink: #5b46e0;
  --lnhw-eyebrow-border: #e0e3ff;

  --lnhw-grad-1: #6366f1;
  --lnhw-grad-2: #a855f7;
  --lnhw-grad-3: #22d3ee;
  --lnhw-accent-grad: linear-gradient(100deg, var(--lnhw-grad-1) 0%, var(--lnhw-grad-2) 52%, var(--lnhw-grad-3) 100%);
  --lnhw-accent: #6d5cf5;
  --lnhw-accent-halo: rgba(139,124,255,.28);

  --lnhw-line: #e7e9f4;
  --lnhw-card-bg: #cfd7ee;        /* --bg-soft; see the header note */
  --lnhw-card-sheen-1: rgba(255,255,255,.05);
  --lnhw-card-sheen-2: rgba(255,255,255,.02);
  --lnhw-card-border-hover: rgba(139,124,255,.4);
  --lnhw-shadow-sm: 0 1px 2px rgba(30,30,80,.05), 0 2px 6px rgba(30,30,80,.05);
  --lnhw-shadow-md: 0 12px 34px rgba(30,30,80,.1);

  --lnhw-halo-color: rgba(139,124,255,.5);
  /* The bloom's outer stop. `transparent` would be equivalent -- CSS gradients
     interpolate in PREMULTIPLIED alpha, so any zero-alpha stop paints the same
     -- but the reference writes the tint at zero alpha and this keeps the
     computed style identical to it, which is what the port is measured against.
     Deliberately not a control for the same reason: re-tinting it cannot change
     a pixel. */
  --lnhw-halo-fade: rgba(139,124,255,0);
  --lnhw-core-ink: #ffffff;
  --lnhw-core-shadow: 0 0 0 10px rgba(139,124,255,.10), 0 22px 52px rgba(99,102,241,.55), inset 0 2px 10px rgba(255,255,255,.4);
  --lnhw-wire-color: rgba(139,124,255,.6);
  --lnhw-wire-glow: rgba(139,124,255,.4);
  --lnhw-n-ink: #ffffff;
  --lnhw-n-shadow: 0 8px 20px rgba(99,102,241,.4);

  position: relative;
  overflow-x: clip;
  padding-top: 112px;
  padding-bottom: 112px;
  background: var(--lnhw-bg);
  font-family: var(--lnhw-font-sans);
  color: var(--lnhw-body);
  font-size: 17px;
  line-height: 1.6;
  font-weight: 400;
  letter-spacing: normal;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* ---- palette · DARK ([data-mode="dark"] on <html>, written by the nav's
   mode toggle). Everything absent here is mode-independent by design — the
   accent gradient, the hub core and the step badge are white-on-gradient in
   both modes, exactly as the reference ships. ---- */
[data-mode="dark"] .lnew-how {
  --lnhw-bg: #0a0a12;
  --lnhw-ink: #f5f6ff;
  --lnhw-body: #b8bed6;
  --lnhw-muted: #949bbb;

  --lnhw-eyebrow-bg: rgba(255,255,255,.05);
  --lnhw-eyebrow-ink: #cdc6ff;
  --lnhw-eyebrow-border: rgba(255,255,255,.12);

  --lnhw-accent: #8b7cff;

  --lnhw-line: rgba(255,255,255,.10);
  --lnhw-card-bg: #10101d;
  --lnhw-shadow-sm: 0 1px 2px rgba(0,0,0,.4);
  --lnhw-shadow-md: 0 12px 34px rgba(0,0,0,.5);
}

/* =====================================================================
   Layout shell
   ===================================================================== */
.lnew-how__wrap {
  width: 100%;
  max-width: var(--lnhw-maxw);
  margin-inline: auto;
  padding-inline: var(--lnhw-gutter);
}

.lnew-how__head {
  max-width: none;
  margin-inline: auto;
  margin-bottom: var(--lnhw-head-gap);
  text-align: center;
}
/* The title carries the 18px that separates it from the eyebrow. With the
   eyebrow hidden it becomes the first child and that gap is dead space. */
.lnew-how__head > :first-child { margin-top: 0; }

/* =====================================================================
   Eyebrow
   ===================================================================== */
.lnew-how__eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--lnhw-font-sans);
  font-size: 13px; font-weight: 600; letter-spacing: .02em;
  color: var(--lnhw-eyebrow-ink);
  background: var(--lnhw-eyebrow-bg);
  border: 1px solid var(--lnhw-eyebrow-border);
  padding: 7px 14px; border-radius: 100px;
}
/* Dim indicator lamp: glows up, fades out, then holds dark. The long unlit
   hold is the point — a continuous sine pulse reads as decoration, a lit/unlit
   cycle with a gap reads as a status light. */
.lnew-how__eyebrow-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--lnhw-accent);
  box-shadow: 0 0 0 4px var(--lnhw-accent-halo);
  opacity: .45;
  animation: lnhw-dot-lamp 5.2s ease-in-out infinite;
}
@keyframes lnhw-dot-lamp {
  0%        { opacity: .35; box-shadow: 0 0 0 4px var(--lnhw-accent-halo), 0 0 0 0 transparent; }
  8%        { opacity: 1;   box-shadow: 0 0 0 4px var(--lnhw-accent-halo), 0 0 7px 1px var(--lnhw-accent); }
  22%       { opacity: 1;   box-shadow: 0 0 0 4px var(--lnhw-accent-halo), 0 0 7px 1px var(--lnhw-accent); }
  40%, 100% { opacity: .35; box-shadow: 0 0 0 4px var(--lnhw-accent-halo), 0 0 0 0 transparent; }
}

/* =====================================================================
   Title + lead
   ===================================================================== */
.lnew-how__title {
  font-family: var(--lnhw-font-head);
  font-weight: 500;
  font-size: var(--lnhw-title-size);
  margin-top: 18px;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--lnhw-ink);
  text-wrap: balance;
}
.lnew-how__title-accent {
  background: var(--lnhw-accent-grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
/* Off by default — the reference head is eyebrow + title only. */
.lnew-how__lead {
  margin-top: 18px;
  font-size: 18px;
  color: var(--lnhw-muted);
  line-height: 1.65;
  text-wrap: balance;
}

/* =====================================================================
   The stage — a fixed-height coordinate box the cards are pinned onto and
   the wires are authored against. See the header note.
   ===================================================================== */
.lnew-how__stage {
  position: relative;
  max-width: var(--lnhw-stage-w);
  height: var(--lnhw-stage-h);
  margin: var(--lnhw-stage-top) auto 0;
}

/* ---- wires: behind everything; the opaque hub + cards hide where they
   cross. preserveAspectRatio="none" on the element (set in the markup) is
   what lets the paths stretch with a narrower stage, and
   vector-effect keeps the stroke a constant 2px while they do. ---- */
.lnew-how__wires {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0; overflow: visible;
  filter: drop-shadow(0 0 var(--lnhw-wire-glow-blur) var(--lnhw-wire-glow));
}
.lnew-how__wire {
  fill: none;
  stroke: var(--lnhw-wire-color);
  stroke-width: var(--lnhw-wire-w);
  vector-effect: non-scaling-stroke;
}

/* =====================================================================
   The hub
   ===================================================================== */
.lnew-how__hub {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: var(--lnhw-hub-size);
  height: var(--lnhw-hub-size);
}
.lnew-how__halo {
  position: absolute;
  inset: calc(-1 * var(--lnhw-halo-spread));
  border-radius: 50%;
  background: radial-gradient(circle, var(--lnhw-halo-color), var(--lnhw-halo-fade) var(--lnhw-halo-stop));
  filter: blur(var(--lnhw-halo-blur));
  animation: lnhw-pulse var(--lnhw-pulse-dur) ease-in-out infinite;
}
.lnew-how--no-pulse .lnew-how__halo { animation: none; }
@keyframes lnhw-pulse {
  0%, 100% { opacity: .8; transform: scale(1); }
  50%      { opacity: 1;  transform: scale(var(--lnhw-pulse-scale)); }
}
.lnew-how__core {
  position: relative;
  width: 100%; height: 100%;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--lnhw-accent-grad);
  color: var(--lnhw-core-ink);
  box-shadow: var(--lnhw-core-shadow);
}
.lnew-how__core svg {
  width: var(--lnhw-hub-glyph);
  height: var(--lnhw-hub-glyph);
  filter: drop-shadow(0 2px 6px rgba(0,0,0,.25));
}
.lnew-how__core img {
  width: var(--lnhw-hub-glyph);
  height: var(--lnhw-hub-glyph);
  object-fit: contain;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,.25));
}

/* =====================================================================
   Step cards.

   ONE positioning rule for every card: --lnhw-f (0..1, written inline by the
   renderer) is the card's fraction across its row, and the row class picks
   the vertical anchor. f=0 -> flush left, f=1 -> flush right, f=.5 -> centred.
   ===================================================================== */
.lnew-how__spoke {
  position: absolute;
  z-index: 2;
  width: var(--lnhw-card-w);
  top: var(--lnhw-row-1);
  left: calc(var(--lnhw-f, .5) * (100% - var(--lnhw-card-w)));
  /* Opaque so the curved wires tuck cleanly BEHIND the cards (a translucent
     surface would let the line show through the glass). */
  background: linear-gradient(160deg, var(--lnhw-card-sheen-1), var(--lnhw-card-sheen-2)), var(--lnhw-card-bg);
  border: var(--lnhw-card-border-w) solid var(--lnhw-line);
  border-radius: var(--lnhw-card-radius);
  padding: var(--lnhw-card-pad-y) var(--lnhw-card-pad-x);
  box-shadow: var(--lnhw-shadow-sm);
  transition: transform .25s var(--lnhw-ease), border-color .25s ease, box-shadow .25s ease;
}
.lnew-how__spoke--r2 { top: var(--lnhw-row-2); }

.lnew-how__spoke:hover {
  transform: translateY(var(--lnhw-card-lift));
  border-color: var(--lnhw-card-border-hover);
  box-shadow: var(--lnhw-shadow-md);
}
/* Only matches when a card carries a link. */
.lnew-how__spoke:focus-visible {
  outline: 2px solid var(--lnhw-accent);
  outline-offset: 3px;
}

.lnew-how__n {
  display: inline-grid; place-items: center;
  width: var(--lnhw-n-size); height: var(--lnhw-n-size);
  border-radius: 50%;
  background: var(--lnhw-accent-grad);
  color: var(--lnhw-n-ink);
  font-family: var(--lnhw-font-head);
  font-weight: 800;
  font-size: var(--lnhw-n-font);
  box-shadow: var(--lnhw-n-shadow);
  margin-bottom: var(--lnhw-n-gap);
}
.lnew-how__n svg {
  width: calc(var(--lnhw-n-size) * .5);
  height: calc(var(--lnhw-n-size) * .5);
}

/* Face / weight / leading / tracking are restated rather than inherited: the
   reference gets them from its own h1-h5 reset, and a WP theme's heading rules
   would otherwise land here instead. text-wrap is restated too: twentytwentyfive
   puts `text-wrap: pretty` directly on h1-h6/p (see [[wp-theme-inherited-type-leak]]),
   and a same-element rule always beats an inherited value no matter how it's
   scoped, so the fix has to sit on these classes rather than a shared ancestor. */
.lnew-how__t {
  font-family: var(--lnhw-font-head);
  font-weight: 500;
  font-size: var(--lnhw-t-size);
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--lnhw-ink);
  margin-bottom: var(--lnhw-t-gap);
  text-wrap: wrap;
}
.lnew-how__d {
  font-size: var(--lnhw-d-size);
  color: var(--lnhw-muted);
  line-height: 1.55;
  text-wrap: wrap;
}

/* =====================================================================
   Stacked layout — drop the radial, stack cleanly under the hub.

   Triggered by lnew-how.js from the SECTION's own width (so a narrow column
   stacks too), or by the no-JS media query at the very bottom of this file.
   ===================================================================== */
.lnew-how--stacked .lnew-how__stage {
  height: auto;
  margin-top: var(--lnhw-stage-top-stacked);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--lnhw-stack-gap);
}
.lnew-how--stacked .lnew-how__wires { display: none; }
.lnew-how--stacked .lnew-how__hub {
  position: static;
  transform: none;
  margin-bottom: var(--lnhw-hub-gap-stacked);
}
.lnew-how--stacked .lnew-how__spoke {
  position: static;
  width: 100%;
  max-width: var(--lnhw-card-w-stacked);
}

/* =====================================================================
   Scroll reveal — --i is assigned by lnew-how.js (index within parent)
   ===================================================================== */
.lnew-how .lnew-reveal {
  opacity: 0;
  transform: translate3d(0, var(--lnhw-rv-dist), 0);
  transition: opacity var(--lnhw-rv-dur) var(--lnhw-ease),
              transform var(--lnhw-rv-dur) var(--lnhw-ease);
  transition-delay: calc(var(--i, 0) * var(--lnhw-rv-step));
}
.lnew-how .lnew-reveal.is-in { opacity: 1; transform: none; }
.elementor-editor-active .lnew-how .lnew-reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .lnew-how .lnew-reveal { opacity: 1; transform: none; transition: none; }
  .lnew-how__halo { animation: none !important; }
  .lnew-how__eyebrow-dot { animation: none !important; opacity: 1; }
  .lnew-how__spoke { transition: none; }
}

/* Coarse pointer: drop the sticky :hover a tap leaves behind. */
@media (hover: none) {
  .lnew-how .lnew-how__spoke:hover {
    transform: none;
    border-color: var(--lnhw-line);
    box-shadow: var(--lnhw-shadow-sm);
  }
}

/* =====================================================================
   No-JS fallback ONLY. lnew-how.js stamps data-lnhw-js on the section as its
   first act, which scopes this block away before it can fight the class it
   manages. The 780px threshold is the reference's own; the widget's "Stack
   Below" control drives the JS path.

   Deliberately a copy of the .lnew-how--stacked block rather than a shared
   selector list: the two live on opposite sides of a media-query boundary and
   CSS has no way to join them.
   ===================================================================== */
@media (max-width: 780px) {
  .lnew-how:not([data-lnhw-js]) .lnew-how__stage {
    height: auto;
    margin-top: var(--lnhw-stage-top-stacked);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--lnhw-stack-gap);
  }
  .lnew-how:not([data-lnhw-js]) .lnew-how__wires { display: none; }
  .lnew-how:not([data-lnhw-js]) .lnew-how__hub {
    position: static;
    transform: none;
    margin-bottom: var(--lnhw-hub-gap-stacked);
  }
  .lnew-how:not([data-lnhw-js]) .lnew-how__spoke {
    position: static;
    width: 100%;
    max-width: var(--lnhw-card-w-stacked);
  }
}
