/* ==========================================================================
   LYNNIAR – ANIMATED ASSETS
   --------------------------------------------------------------------------
   Contents
     1. Shared tokens
     2. Shared keyframes
     3. Shared helpers (icons, motion layers)
     4. No Pay Per Ping card      template-parts/lynniar/ping-card.php
     5. Lynniar hub               template-parts/lynniar/hub.php
     6. How it works (scroll)     template-parts/lynniar/how-it-works.php
     7. Globe                     template-parts/lynniar/globe.php
     8. Reduced motion
     9. Off-screen pausing        (must stay last – see note)

   Performance rules followed throughout
     • Only transform, opacity, stroke-dashoffset and clip-path are animated.
       Nothing that changes layout (width, height, top…) ever animates.
     • Anything that moves lives in its own "motion layer", so the browser
       repaints only that layer, never the text and shapes around it.
     • Looping animations carry the class .lyn-loop. lynniar-assets.js adds
       .is-offscreen to an asset when it scrolls out of view, which pauses
       every loop inside it (section 9).
   ========================================================================== */

/* ==========================================================================
   1. SHARED TOKENS
   Each value points at the SupaGroup design-system variable first and falls
   back to the raw hex, so the assets still look right if a page doesn't
   load the design-system stylesheet.
   ========================================================================== */

.lyn {
  --lyn-blue-deep: var(--sg-blue-700, #024ade);
  --lyn-blue: var(--sg-blue-600, #1b66f8);
  --lyn-blue-light: var(--sg-blue-400, #3096ff);
  --lyn-blue-tint: var(--sg-blue-100, #d8e5ff);

  --lyn-heading: var(--sg-ink-950, #0f0f0f);
  --lyn-ink: var(--sg-ink-900, #1c1c1c);
  --lyn-body: var(--sg-ink-800, #242424);
  --lyn-ink-soft: var(--sg-ink-600, #4f4f4f);
  --lyn-outline: var(--sg-outline, #c4c4c4);
  --lyn-surface: var(--sg-surface, #e4e9f3);
  --lyn-white: #ffffff;
  --lyn-on-brand: var(--sg-inverse-on-surface, #f9fbff);

  --lyn-gradient: var(
    --gradient-brand,
    linear-gradient(180deg, #1b66f8 0%, #024ade 100%)
  );

  --lyn-font-display: var(
    --font-display,
    "Google Sans Flex",
    system-ui,
    sans-serif
  );
  --lyn-font-body: var(--font-body, Figtree, system-ui, sans-serif);
}

/* ==========================================================================
   2. SHARED KEYFRAMES
   stroke-dashoffset values are unitless SVG user units.
   ========================================================================== */

/* A single dot travelling along a path.
   The path has pathLength="100", so 0 → -100 means "start → end". */
@keyframes lyn-travel {
  0% {
    stroke-dashoffset: 0;
    opacity: 0;
  }
  12% {
    opacity: 1;
  }
  88% {
    opacity: 1;
  }
  100% {
    stroke-dashoffset: -100;
    opacity: 0;
  }
}

/* A ring that grows outward and fades away. */
@keyframes lyn-pulse {
  from {
    transform: scale(1);
    opacity: 0.8;
  }
  to {
    transform: scale(1.5);
    opacity: 0;
  }
}

/* Dashed or dotted lines that "march" forward along their path.
   32 is a whole number of repeats for both dash patterns we use
   (8 8 repeats every 16, 0.5 7.5 repeats every 8), so the loop is seamless. */
@keyframes lyn-march {
  from {
    stroke-dashoffset: 0;
  }
  to {
    stroke-dashoffset: -32;
  }
}

/* ==========================================================================
   3. SHARED HELPERS
   ========================================================================== */

/* Line icons (inline SVG or nested <svg> inside a diagram).
   They draw in the current text colour, so set `color` on a parent. */
.lyn-icon {
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Diagrams are two SVGs stacked on top of each other:
     .lyn-layer-motion      – underneath; everything that animates
     .lyn-layer-foreground  – on top; static circles, pills and labels
   The motion layer sets the size (it's in normal flow); the foreground
   is laid exactly over it. Both share the same viewBox. */
.lyn-diagram {
  position: relative;
  width: 100%;
}

.lyn-layer-motion {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible; /* pulse rings can grow past the edge of the drawing */
  /* Give the moving layer its own GPU layer, so each frame only
     repaints this SVG and not the text and shapes above it. */
  will-change: transform;
}

.lyn-layer-foreground {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* The travelling dot. It's a copy of the curve it rides on, drawn as a
   single tiny dash with round caps – which renders as a perfect circle.
   The stroke-width sets the dot's diameter. */
.lyn-dot {
  fill: none;
  stroke-linecap: round;
  stroke-dasharray: 0.01 200;
  opacity: 0;
  animation: lyn-travel 2.4s linear infinite;
}

/* Rings that pulse outward from their own centre. */
.lyn-pulse {
  fill: none;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: lyn-pulse 2.4s ease-out infinite;
}

/* ==========================================================================
   4. NO PAY PER PING CARD
   Design: 710 × 434 desktop (6a), 390 mobile (2b).
   Two diagram versions are in the markup: a wide one and a compact one.
   A container query shows one and hides the other (display:none also stops
   the hidden one's animations, so it costs nothing).
   ========================================================================== */

.lyn-ping__card {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.lyn-ping {
  container: lyn-ping / inline-size;
  max-width: 710px;
}

.lyn-ping__diagram--compact {
  display: none;
}

.lyn-ping__text {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 32px 32px 36px;
}

.lyn-ping__title {
  margin: 0;
  font: 400 36px/1 var(--font-display);
  color: var(--lyn-white);
}

.lyn-ping__body {
  margin: 0;
  font: 400 16px/1.5 var(--lyn-font-body);
  color: var(--lyn-on-brand);
}

/* Diagram parts */
.lyn-ping__curve {
  fill: none;
  stroke: var(--lyn-blue-light);
  stroke-width: 1.5;
}

.lyn-ping .lyn-dot {
  stroke: var(--lyn-white);
}

.lyn-ping .lyn-pulse {
  stroke: var(--lyn-blue-light);
  stroke-width: 2;
}

.lyn-ping__hub-circle {
  fill: var(--lyn-white);
  stroke: var(--lyn-blue-light);
}

.lyn-ping__hub-label {
  font: 500 20px var(--lyn-font-display);
  fill: var(--lyn-blue-deep);
}

.lyn-ping__buyer-pill {
  fill: var(--lyn-white);
}

.lyn-ping__buyer {
  color: var(--lyn-blue-deep); /* icon colour */
}

.lyn-ping__buyer-label {
  font: 400 20px var(--font-display);
  fill: var(--color-primaryContainter);
}
/* Compact card (below 520px wide) – matches mobile design 2b */
@container lyn-ping (max-width: 519px) {
  .lyn-ping__diagram--wide {
    display: none;
  }

  .lyn-ping__diagram--compact {
    display: block;
    max-width: 360px;
  }

  .lyn-ping__text {
    gap: 8px;
    padding: 0;
  }

  .lyn-ping__title {
    font-size: 32px;
  }

  .lyn-ping__diagram--compact .lyn-ping__hub-label {
    font-size: 15px;
  }

  .lyn-ping__diagram--compact .lyn-ping__buyer-label {
    font-size: 14px;
  }
}

/* ==========================================================================
   5. LYNNIAR HUB
   Design: 680 × 548 (6b). Scales down proportionally with its container.
   ========================================================================== */

.lyn-hub {
  max-width: 680px;
  margin-inline: auto;
}

/* Motion layer */
.lyn-hub__orbit {
  fill: none;
  stroke: var(--lyn-blue);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-dasharray: 0.5 7.5; /* round caps turn tiny dashes into dots */
  animation: lyn-march 3s linear infinite; /* moving forward along a circle = clockwise */
}

.lyn-hub__spoke {
  fill: none;
  stroke: var(--lyn-blue-tint);
  stroke-width: 1.5;
}

.lyn-hub__flow {
  fill: none;
  stroke: var(--lyn-blue);
  stroke-width: 1.5;
  stroke-dasharray: 8 8;
  animation: lyn-march 1.2s linear infinite; /* both paths are drawn left → right */
}

.lyn-hub .lyn-dot {
  stroke: var(--color-onPrimaryContainer);
}

.lyn-hub .lyn-pulse {
  stroke: var(--lyn-blue-light);
  stroke-width: 2;
}

/* Foreground layer */
.lyn-hub__ring {
  fill: var(--lyn-white);
  stroke: var(--lyn-blue-light);
  stroke-width: 6;
}

.lyn-hub__ring-label {
  font: 500 32px var(--lyn-font-display);
  fill: var(--lyn-blue-deep);
}

.lyn-hub__pill {
  fill: var(--lyn-ink);
  stroke: var(--lyn-ink-soft);
  stroke-width: 1;
}

.lyn-hub__module {
  color: var(--lyn-white); /* icon colour */
}

.lyn-hub__pill-label {
  font: 400 16px var(--lyn-font-display);
  fill: var(--lyn-white);
}

.lyn-hub__side {
  color: var(--lyn-blue); /* icon colour */
}

.lyn-hub__side-label {
  font: 400 18px var(--lyn-font-display);
  fill: var(--lyn-blue);
}
.lyn-hub__side-pill {
  fill: var(--lyn-white); /* = background colour */
  stroke: var(--lyn-blue); /* = border colour */
  stroke-width: 1.5; /* = border width */
}

/* ==========================================================================
   6. HOW IT WORKS (scroll-linked)
   Design: desktop 6c, mobile 3c.

   How the scroll effect works
   ---------------------------
   • A "reading line" sits 40% of the way down the viewport.
   • lynniar-assets.js watches each step's node with an IntersectionObserver
     (no scroll listener). When a node passes the reading line, every
     element with that step's data-step gets .is-reached.
   • The blue spine fill follows the reading line smoothly using a CSS
     scroll-driven animation, which runs off the main thread. In browsers
     without support, the JS sets --lyn-spine-progress instead and the fill
     glides from node to node.
   • All of the dimmed / un-filled states only apply once the JS has added
     .is-ready. With no JS or with reduced motion, the whole diagram simply
     shows at full strength.

   Layout
   ------
   Mobile-first measurements (design 3c) are set as custom properties on
   .lyn-how__diagram; the desktop measurements (design 6c) replace them
   from 680px up. Everything else is calculated from these values.
   ========================================================================== */

.lyn-how {
  container: lyn-how / inline-size;
  /* Distance from the top of the viewport to the sticky index.
     Increase this if your site header is fixed and taller than 96px. */
  --lyn-sticky-top: 160px;
}

/* ---- Header ---- */

.lyn-how__title {
  margin: 0;
  font: 400 36px/1.1 var(--lyn-font-display);
  color: var(--lyn-heading);
}

.lyn-how__intro {
  max-width: 440px;
  margin: 16px 0 0;
  font: 400 16px/1.5 var(--lyn-font-body);
  color: var(--lyn-body);
}

.lyn-how__rule {
  height: 0;
  margin: 32px 0;
  border: 0;
  border-top: 1px solid var(--lyn-outline);
}

/* ---- Layout: stacked by default, two columns from 1024px ---- */

.lyn-how__layout {
  display: grid;
  gap: 48px;
}

/* ---- Step index (left column) ---- */

.lyn-how__index {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.lyn-how__index-item {
  display: flex;
  align-items: center;
  gap: 16px;
  font: 400 20px/1.2 var(--lyn-font-display);
  color: var(--lyn-ink);
}

.lyn-how__index-number {
  display: grid;
  flex: none;
  place-items: center;
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  border: 1px solid var(--lyn-blue);
  border-radius: 50%;
  background: var(--lyn-white);
  font: 500 14px/1 var(--lyn-font-display);
  color: var(--lyn-blue);
  transition:
    background-color 300ms ease,
    color 300ms ease;
}

.lyn-how__index-item.is-current .lyn-how__index-number {
  background: var(--lyn-blue);
  color: var(--lyn-white);
}

/* ---- Diagram (right column) – mobile measurements ---- */

.lyn-how__diagram {
  --lyn-spine-x: 8px; /* centre of the vertical spine */
  --lyn-node-size: 16px; /* ring on the spine for each step */
  --lyn-card-indent: 32px; /* where step cards start */
  --lyn-connector-gap: 0px; /* space between a connector and its card */
  --lyn-card-width: none; /* none = fill the column */
  --lyn-step-card-height: 72px;
  --lyn-hub-height: 64px;

  --lyn-branch-x: 40px; /* dotted line feeding the add-on modules */
  --lyn-module-indent: 56px; /* where module cards start */
  --lyn-module-width: none;
  --lyn-module-height: 136px;
  --lyn-module-gap: 8px;

  --lyn-gap-before-hub: 32px; /* vertical gaps between rows */
  --lyn-gap-before-modules: 16px;
  --lyn-gap-before-filtering: 48px;
  --lyn-gap-before-buyers: 32px;

  position: relative;
  display: flex;
  flex-direction: column;
}

/* The spine: a light dotted line from the first node to the last.
   Nodes sit in the middle of their cards, so the spine starts and ends
   half a card-height in from the top and bottom. */
.lyn-how__spine {
  position: absolute;
  top: calc(var(--lyn-step-card-height) / 2);
  bottom: calc(var(--lyn-step-card-height) / 2);
  left: calc(var(--lyn-spine-x) - 0.75px);
  width: 0;
  border-left: 1.5px dotted var(--lyn-blue-tint);
}

/* The blue fill sits exactly over the spine. It's always full length;
   clip-path hides the part below the reading line, so the dots never
   get squashed the way scaling would squash them. */
.lyn-how__spine-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -1.5px;
  border-left: 1.5px dotted var(--lyn-blue);
}

/* Each step row: node + connector on the left, card on the right. */
.lyn-how__step {
  position: relative;
  padding-left: var(--lyn-card-indent);
}

.lyn-how__step--hub {
  margin-top: var(--lyn-gap-before-hub);
}
.lyn-how__step--filtering {
  margin-top: var(--lyn-gap-before-filtering);
}
.lyn-how__step--buyers {
  margin-top: var(--lyn-gap-before-buyers);
}

/* The ring on the spine. Centred vertically on its card.
   The ring is an inset shadow rather than a border, so the connector
   below can start exactly at the ring's outer edge. */
.lyn-how__node {
  position: absolute;
  top: calc(50% - var(--lyn-node-size) / 2);
  left: calc(var(--lyn-spine-x) - var(--lyn-node-size) / 2);
  width: var(--lyn-node-size);
  height: var(--lyn-node-size);
  border-radius: 50%;
  background: var(--lyn-white);
  box-shadow: inset 0 0 0 2px var(--lyn-blue);
}

/* Dotted connector from the node across to the card. */
.lyn-how__node::after {
  content: "";
  position: absolute;
  top: calc(50% - 0.75px);
  left: 100%;
  width: calc(
    var(--lyn-card-indent) - var(--lyn-spine-x) - var(--lyn-node-size) / 2 -
      var(--lyn-connector-gap)
  );
  border-top: 1.5px dotted var(--lyn-blue);
}

/* Step cards: Traffic, Filtering & Routing, Your Buyers */
.lyn-how__card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  box-sizing: border-box;
  max-width: var(--lyn-card-width);
  height: var(--lyn-step-card-height);
  padding: 16px;
  border: 1px solid var(--lyn-ink);
  border-radius: 16px;
  background: var(--lyn-white);
  color: var(--lyn-ink);
}

.lyn-how__card-title {
  margin: 0;
  font: 500 16px/1.5 var(--lyn-font-display);
  color: var(--lyn-ink);
}

/* The Lynniar card: light blue, cross-fading to the brand gradient
   (opacity only, so the fade is cheap). */
.lyn-how__hub {
  position: relative;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  max-width: var(--lyn-card-width);
  height: var(--lyn-hub-height);
  padding: 0 16px;
  overflow: hidden;
  border-radius: 16px;
  background: var(--lyn-blue-tint);
}

.lyn-how__hub-gradient {
  position: absolute;
  inset: 0;
  background: var(--lyn-gradient);
  transition: opacity 400ms ease;
}

.lyn-how__hub-label {
  position: relative; /* sits above the gradient */
  font: 500 28px/1 var(--lyn-font-display);
  color: var(--lyn-white);
}

/* Add-on modules: Email, Offer Wall, Reporting */
.lyn-how__modules {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--lyn-module-gap);
  margin: var(--lyn-gap-before-modules) 0 0;
  padding: 0 0 0 var(--lyn-module-indent);
  list-style: none;
}

/* The branch: from the bottom of the Lynniar card down to the middle
   of the last module. */
.lyn-how__modules::before {
  content: "";
  position: absolute;
  top: calc(var(--lyn-gap-before-modules) * -1);
  bottom: calc(var(--lyn-module-height) / 2);
  left: calc(var(--lyn-branch-x) - 0.75px);
  border-left: 1.5px dotted var(--lyn-blue);
}

.lyn-how__module {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-sizing: border-box;
  max-width: var(--lyn-module-width);
  min-height: var(--lyn-module-height);
  padding: 16px;
  border: 1px solid var(--lyn-blue);
  border-radius: 16px;
  background: var(--lyn-white);
  color: var(--lyn-blue);
}

/* Short connector from the branch across to each module. */
.lyn-how__module::before {
  content: "";
  position: absolute;
  top: calc(50% - 0.75px);
  left: calc(var(--lyn-branch-x) - var(--lyn-module-indent));
  width: calc(var(--lyn-module-indent) - var(--lyn-branch-x));
  border-top: 1.5px dotted var(--lyn-blue);
}

.lyn-how__module-title {
  margin: 0;
  font: 500 18px/24px var(--lyn-font-display);
  color: var(--lyn-blue);
}

.lyn-how__module-text {
  margin: 4px 0 0;
  font: 400 14px/1.5 var(--lyn-font-body);
  color: var(--lyn-body);
}

/* Keyboard focus for the cards */
.lyn-how__card:focus-visible,
.lyn-how__hub:focus-visible,
.lyn-how__module:focus-visible {
  outline: 2px solid var(--lyn-blue-deep);
  outline-offset: 3px;
}

/* Return path from Email / Offer Wall back up to Traffic.
   Desktop only – hidden on the narrow layout, as in design 3c. */
.lyn-how__return {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  width: 680px;
  height: 960px;
  overflow: visible;
  pointer-events: none;
}

.lyn-how__return-path {
  fill: none;
  stroke: var(--lyn-blue);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-dasharray: 0.5 7.5;
  /* "reverse" makes the dots travel back towards Traffic.
     Paused until the modules are revealed (see scroll states below). */
  animation: lyn-march 2.4s linear infinite reverse;
  animation-play-state: paused;
}

/* ---- Desktop measurements (680px and wider) – design 6c ---- */

@container lyn-how (min-width: 680px) {
  .lyn-how {
    padding-block: 96px;
  }

  .lyn-how__title {
    font-size: 48px;
    line-height: 1.05;
  }

  .lyn-how__rule {
    margin: 48px 0;
  }

  .lyn-how__index {
    gap: 16px;
  }

  .lyn-how__index-item {
    font-size: 28px;
  }

  .lyn-how__index-number {
    width: 40px;
    height: 40px;
    border-width: 1.5px;
    font-size: 18px;
    font-weight: 400;
  }

  .lyn-how__diagram {
    --lyn-spine-x: 68px;
    --lyn-node-size: 18px;
    --lyn-card-indent: 163px;
    --lyn-connector-gap: 10px;
    --lyn-card-width: 434px;
    --lyn-step-card-height: 90px;
    --lyn-hub-height: 74px;

    --lyn-branch-x: 200px;
    --lyn-module-indent: 228px;
    --lyn-module-width: 371px;
    --lyn-module-height: 120px;
    --lyn-module-gap: 16px;

    --lyn-gap-before-hub: 64px;
    --lyn-gap-before-modules: 32px;
    --lyn-gap-before-filtering: 64px;
    --lyn-gap-before-buyers: 64px;

    width: 680px;
  }

  .lyn-how__card-title {
    font: 400 20px/1.2 var(--lyn-font-display);
  }

  .lyn-how__hub {
    padding: 0 32px;
  }

  .lyn-how__hub-label {
    font-size: 32px;
    font-weight: 400;
  }

  .lyn-how__module {
    height: auto; /* fixed on desktop: the return path lines up with these */
  }

  .lyn-how__module-title {
    font: 400 20px/1.2 var(--lyn-font-display);
  }

  .lyn-how__return {
    display: block;
  }
}

/* ---- Two columns (1024px and wider) – index sticks while you scroll ---- */

@container lyn-how (min-width: 1024px) {
  .lyn-how__layout {
    grid-template-columns: minmax(0, 560px) 680px;
    justify-content: space-between;
    align-items: start;
    gap: 0;
  }

  .lyn-how__index {
    position: sticky;
    top: var(--lyn-sticky-top);
  }
}

/* ---- Scroll states (only once the JS has added .is-ready) ---- */

/* Steps start dimmed … */
.lyn-how.is-ready .lyn-how__fade {
  opacity: 0.35;
  transition: opacity 300ms ease;
}

/* … and come up to full strength when their step is reached. */
.lyn-how.is-ready .is-reached .lyn-how__fade,
.lyn-how.is-ready .lyn-how__fade.is-reached {
  opacity: 1;
}

/* Lynniar card: light blue until step 2 is reached, then brand gradient. */
.lyn-how.is-ready .lyn-how__hub-gradient {
  opacity: 0;
}

.lyn-how.is-ready .is-reached .lyn-how__hub-gradient {
  opacity: 1;
}

/* Return path dots start flowing once the modules are revealed. */
.lyn-how__return.is-reached .lyn-how__return-path {
  animation-play-state: running;
}

/* Spine fill – fallback version.
   The JS sets --lyn-spine-progress (0 to 1) to the node that was last
   reached, and the fill glides there. */
.lyn-how.is-ready .lyn-how__spine-fill {
  clip-path: inset(0 0 calc(100% - var(--lyn-spine-progress, 0) * 100%) 0);
  transition: clip-path 300ms ease-out;
}

@keyframes lyn-spine-fill {
  from {
    clip-path: inset(0 0 100% 0);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}

/* Spine fill – scroll-driven version (Chrome, Edge, Safari 26+).
   A view timeline on the spine tracks it crossing the reading line:
     • view-timeline-inset trims the bottom 60% off the viewport, so the
       viewport's "end" edge becomes the reading line at 40%.
     • entry-crossing 0%   = top of the spine reaches the reading line
       entry-crossing 100% = bottom of the spine reaches the reading line
   The fill tracks the reading line exactly, without any JavaScript. */
@supports (animation-timeline: view()) {
  .lyn-how.is-ready .lyn-how__spine {
    view-timeline-name: --lyn-spine;
    view-timeline-axis: block;
    view-timeline-inset: 0px 60%;
  }

  .lyn-how.is-ready .lyn-how__spine-fill {
    transition: none;
    animation-name: lyn-spine-fill;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: --lyn-spine;
    animation-range: entry-crossing 0% entry-crossing 100%;
  }
}

/* ==========================================================================
   7. GLOBE
   Design: 605 × 605 (4d), 358 mobile (4e) – one asset, scales down fluidly.
   No background or padding: the globe fills the box edge to edge.
   The map itself is a pre-rendered image (globe-base.svg), so the browser
   draws it once. Only the small arc/marker overlay on top animates.
   ========================================================================== */

@keyframes lyn-globe-march {
  from {
    stroke-dashoffset: 0;
  }
  to {
    stroke-dashoffset: -20;
  } /* two repeats of the 4 6 dash pattern */
}

@keyframes lyn-globe-pulse {
  from {
    transform: scale(1);
    opacity: 0.7;
  }
  to {
    transform: scale(2.6);
    opacity: 0;
  }
}

.lyn-globe {
  position: relative;
  /* width: 100% matters: without it, a column that centres its content
     (common on mobile) shrinks the globe to nothing.
     Change max-width to "none" to let it fill wider columns. */
  width: 100%;
  max-width: 605px;
  margin: 0 auto; /* centred if the column is wider than max-width */
}

/* The map image sits in normal flow, so it gives the globe its size. */
.lyn-globe__base {
  display: block;
  width: 100%;
  height: auto;
}

/* The animated arcs and markers are laid exactly over the map. */
.lyn-globe__overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible; /* pulse rings can grow past the edge */
  will-change: transform; /* own GPU layer: repainting arcs never touches the map */
}

.lyn-globe__arc {
  stroke: var(--lyn-blue);
  stroke-width: 1;
  stroke-dasharray: 4 6;
  opacity: 0.5;
  animation: lyn-globe-march 1.6s linear infinite; /* dashes flow out from the UK */
}

.lyn-globe__pulse {
  transform-box: fill-box;
  transform-origin: center;
  animation: lyn-globe-pulse 2.4s ease-out infinite;
}

/* ==========================================================================
   8. REDUCED MOTION
   Every loop stops. The travelling dots and pulse rings have opacity 0
   when they're not animating, so they simply disappear. "How it works"
   never gets .is-ready (the JS checks this setting too), so every step
   shows at full strength.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .lyn-loop {
    animation: none;
  }
}

/* ==========================================================================
   9. OFF-SCREEN PAUSING
   lynniar-assets.js adds .is-offscreen to an asset while it's out of view.
   This rule must stay at the end of the file: it has the same specificity
   as the "start the return path" rule above and needs to win over it.
   ========================================================================== */

.lyn.is-offscreen .lyn-loop {
  animation-play-state: paused;
}
