/* Referral-flow diagram — self-contained module ported from Paul's
   test-framework build (Figma 717:8746). Scoped under .tlm-referral-flow
   so it does NOT inherit or pollute the shared theme cascade. Fixed
   1280x602 coordinate system scaled by JS (referral-flow.js). The old
   referral-journey.css carousel module is unrelated and untouched. */

/* ============================================================
   test-framework.html — ISOLATED. No theme inheritance.
   Referral flow graphic rebuilt from Figma 717:8746.
   Build marker: v1
   ============================================================ */

.tlm-referral-flow{
  --bg:        #f9f8f4;   /* panel background (cream)        */
  --white:     #ffffff;
  --navy:      #09163b;
  --navy-2:    #0b1d35;
  --ink:       #192b37;
  --pink:      #ff007c;
  --green:     #12b76a;   /* success check                  */
  --green-bg:  #ecfdf3;
  --green-tx:  #027a48;
  --red:       #ff3b30;   /* close cross                    */
  --line:      #cfd4dc;   /* connector stroke               */
  --muted:     rgba(25,43,55,.76);

  --serif: "Halant", Georgia, "Times New Roman", serif;
  --sans:  "Inter", -apple-system, "Segoe UI", system-ui, sans-serif;
  --mono:  "Fragment Mono", "SFMono-Regular", ui-monospace, Menlo, monospace;

  --shadow-card: 0 1px 3px rgba(0,0,0,.10), 0 1px 2px rgba(0,0,0,.06);
  --shadow-soft: 0 1px 1.5px rgba(0,0,0,.10), 0 1px 1px rgba(0,0,0,.06);
}

/* Scoped box-sizing so the module's px geometry is reliable without
   touching the global reset. */
.tlm-referral-flow,
.tlm-referral-flow *,
.tlm-referral-flow *::before,
.tlm-referral-flow *::after{ box-sizing:border-box; }

/* The cream background + rounded corners live on the wrapper so they
   fill the full container width. The graphic itself (.panel) stays at
   its design size and centres within, with the spare width as padding. */
.tlm-referral-flow{
  font-family:var(--sans);
  color:var(--navy);
  -webkit-font-smoothing:antialiased;
  margin-top:var(--space-xl, 48px);
  background:var(--bg);
  border-radius:16px;
  padding:48px;
}

/* subtitle above the diagram (desktop only — hidden on mobile below).
   The panel has ~85px of baked-in whitespace above the eyebrow row, so a
   negative bottom margin pulls the subtitle down to a 48px gap (= the
   wrapper's top padding, our 1x baseline). */
.tlm-referral-flow__subtitle{
  margin:0 0 -37px;
  text-align:center;
  font-family:var(--serif);
  font-weight:500;
  font-size:24px;
  letter-spacing:-.01em;
  color:var(--navy);
}

/* byline below the diagram (desktop only — hidden on mobile below).
   The panel has ~105px of baked-in whitespace below the last card, so a
   negative top margin pulls the byline up to a 48px gap from the cards
   (= the 1x baseline). */
.tlm-referral-flow__byline{
  margin:-57px auto 0;
  text-align:center;
  /* Wrap to two centred lines when the single-line width won't fit the
     panel (was `nowrap`, which spilled the sentence past the panel's
     right edge in the 1024-1300 band). Stays one line at wide desktop. */
  max-width:100%;
  font-family:var(--sans);
  font-size:16px;
  line-height:1.6;
  color:var(--muted);
}
.tlm-referral-flow__byline strong{ font-weight:700; }


/* ---- the graphic itself: design-width, centred, transparent -------- */
.panel{
  position:relative;
  width:100%;
  max-width:1280px;
  margin-inline:auto;
  aspect-ratio:1280/602;
  overflow:hidden;
}

/* The diagram is authored on a fixed 1280x602 coordinate system and
   scaled by the panel. Everything inside .stage is absolutely placed
   in those px so it tracks the Figma layout exactly. */
.stage{
  position:absolute;
  inset:0;
  container-type:inline-size;
}
.stage > *{ position:absolute; }

/* px -> cqw helper isn't used; we scale the whole stage with a transform
   wrapper so child px == design px. */
.scaler{
  position:absolute;
  top:0; left:0;
  width:1280px; height:602px;
  transform-origin:top left;
}
.scaler > *{ position:absolute; }

/* ===================================================================
   COLUMN HEADERS
   =================================================================== */
.eyebrow{
  display:flex; align-items:center; gap:9px;
  font-family:"Proxima Nova", var(--sans);
  font-weight:700;
  font-size:16px;
  letter-spacing:.085em;
  text-transform:uppercase;
  color:var(--navy);
  white-space:nowrap;
}
.eyebrow .dot{
  width:8px; height:8px; border-radius:50%;
  background:var(--navy); flex:none;
  margin-top:-1px;             /* Figma dot sits a hair above caps centre */
}
.eyebrow .hl{ color:var(--pink); }

.eyebrow.ask     { left:111px; top:92px; }
.eyebrow.look    { left:539px; top:92px; }
.eyebrow.contact { left:933px; top:92px; }

/* ===================================================================
   CONNECTORS (SVG, single overlay)
   =================================================================== */
.connectors{
  left:0; top:0; width:1280px; height:602px;
  pointer-events:none;
  overflow:visible;
}

/* ===================================================================
   COLUMN 1 — ASK (referral quote card)
   =================================================================== */
.card-ask{
  left:64px; top:141px;
  width:285px; height:347px;
  background:var(--white);
  border-radius:13px;
  box-shadow:var(--shadow-soft);
  padding:35px 20px;
  display:flex; flex-direction:column; justify-content:space-between;
}
.quote-bubble{
  position:relative;
  background:var(--bg);
  border-radius:16px;
  padding:16px;
  font-family:var(--serif);
  font-size:18px;
  line-height:1.4;
  letter-spacing:-.01em;
  color:var(--navy);
}
.quote-bubble::after{
  content:"";
  position:absolute;
  left:32px; bottom:-9px;
  width:18px; height:18px;
  background:var(--bg);
  transform:rotate(45deg);
  border-radius:0 0 4px 0;
}
.byline{ display:flex; align-items:center; gap:8px; }
.byline img{
  width:38px; height:38px; border-radius:50%;
  object-fit:cover; flex:none;
  background:#ddd;
}
.byline .name{ font-weight:600; font-size:16px; line-height:1.5; color:var(--navy); }
.byline .role{ font-size:11px; line-height:1.5; color:var(--navy-2); }

/* ===================================================================
   COLUMN 2 — LOOK (browser evidence stack)
   =================================================================== */
.card-look{
  left:484px; top:141px;
  width:312px; height:347px;
  background:var(--white);
  border-radius:12px;
  box-shadow:var(--shadow-card);
  overflow:hidden;
}
/* the inner browser-window mock */
.browser{
  position:absolute;
  left:16px; top:16px;
  width:279px;
  border:1px solid #f1f1f1;
  border-radius:9px;
  overflow:hidden;
  background:var(--white);
}
/* address bar shell */
.browser__bar{
  background:#f3f2ef;
  padding:14px;
}
.browser__url{
  display:flex; align-items:center; gap:6px;
  background:var(--white);
  border-radius:56px;
  height:27px;
  padding:0 11px;
  font-size:9px; font-weight:500; color:var(--navy);
  letter-spacing:-.01em;
}
.browser__url svg{ flex:none; }
/* page content inside the browser */
.browser__page{ padding:16px 10px; background:var(--white); }

/* mini firm-site nav */
.firmnav{
  display:flex; align-items:center; justify-content:space-between;
  margin-bottom:12px;
}
.firmnav__brand{ display:flex; align-items:center; gap:3px; }
.firmnav__brand .mark{ width:8px; height:8px; }
.firmnav__brand .mark path{ fill:#ff2a00; }
.firmnav__brand .nm{ font-size:6px; font-weight:600; letter-spacing:-.03em; color:#000; }
.firmnav__links{ display:flex; align-items:center; gap:10px; }
.firmnav__links span{ font-size:3.6px; color:var(--muted); }
.firmnav__cta{
  background:#141414; color:#fff;
  font-size:3.6px; font-weight:500;
  padding:3px 8px; border-radius:16px;
}

/* profile block */
.profile{
  display:flex; gap:16px;
  background:#f6f6f1;
  border-radius:2px;
  padding:24px 12px;
}
.profile__col{ flex:1 1 0; min-width:0; }
.profile__role{
  font-size:4px; font-weight:700; letter-spacing:.1em;
  color:#ff2a00; margin:0 0 4px;
}
.profile__name{
  font-size:12px; font-weight:500; letter-spacing:-.03em;
  color:var(--ink); margin:0 0 4px; line-height:1.1;
}
.profile__bio{
  font-size:5px; line-height:1.6; color:var(--muted);
  margin:0 0 9px; letter-spacing:-.02em;
}
.profile__contacts{ display:flex; flex-direction:column; gap:6px; }
.profile__row{ display:flex; align-items:center; gap:3px; }
.profile__row svg{ width:6.5px; height:6.5px; flex:none; }
.profile__row b{ font-size:4.5px; font-weight:700; color:var(--ink); }
.profile__row span{ font-size:4.5px; color:var(--ink); }
.profile__rule{ height:1px; background:#e3e1da; }
.profile__photo{
  width:108px; flex:none;
  border-radius:2px; overflow:hidden;
  align-self:stretch;
  background:#cfc9c0;
}
.profile__photo img{ width:100%; height:100%; object-fit:cover; display:block; }

/* floating "P&S advise..." card */
.evidence-pop{
  position:absolute;
  left:97px; top:209px;
  width:188px;
  background:var(--white);
  border-radius:6px;
  box-shadow:0 10px 25px rgba(0,0,0,.10);
  padding:19px 16px;
}
.evidence-pop__title{
  margin:0 0 11px;
  font-family:var(--serif);
  font-size:12px; line-height:1.35; letter-spacing:-.02em;
  color:var(--navy); font-weight:400;
}
.evidence-pop p{
  margin:0;
  font-size:9px; line-height:1.4; letter-spacing:-.02em;
  color:var(--muted);
}

/* ===================================================================
   COLUMN 3 — CONTACT (call card + browser-result card)
   =================================================================== */
.col-contact{
  left:936px; top:141px;
  width:285px; height:347px;
  display:flex; flex-direction:column; justify-content:space-between;
}

/* in-progress call card */
.call{
  background:var(--white);
  border-radius:10px;
  box-shadow:var(--shadow-soft);
  padding:21px 22px;
  height:158px;                /* centre lands on the green-check arrow (y220) */
}
.call__head{ display:flex; align-items:center; gap:9px; }
.call__icon{
  width:41px; height:41px; border-radius:50%;
  background:var(--navy);
  display:flex; align-items:center; justify-content:center;
  flex:none;
}
.call__icon svg{ width:20px; height:20px; }
.call__id{ flex:1 1 0; min-width:0; }
.call__name{
  font-family:var(--serif); font-weight:500;
  font-size:20px; line-height:1.2; letter-spacing:-.01em;
  color:var(--navy);
}
.badge{
  display:inline-flex; align-items:center; gap:3px;
  background:var(--green-bg);
  border-radius:12px;
  padding:1.5px 6px 1.5px 4.5px;
  margin-top:2px;
}
.badge .dot{ width:5px; height:5px; border-radius:50%; background:var(--green-tx); }
.badge span{ font-size:9px; font-weight:500; color:var(--green-tx); }
.call__time{
  font-family:var(--mono);
  font-size:11px; letter-spacing:-.01em;
  color:var(--navy); opacity:.8; align-self:flex-start;
}
.waveform{
  margin-top:24px;
  background:var(--bg);
  border-radius:8px;
  box-shadow:0 1px 2px rgba(16,24,40,.05);
  height:43px;
  padding:0 16px;
  display:flex; align-items:center; justify-content:space-between;
}
.waveform i{
  display:block;
  width:2px;
  background:#9b9b98;
  border-radius:11px;
  flex:none;
}

/* browser-result card (the "no answer / second outcome" tile) */
.result{
  position:relative;
  background:var(--white);
  border-radius:10px;
  box-shadow:var(--shadow-card);
  overflow:hidden;
  height:158px;                /* centre lands on the red-cross arrow (y409) */
}
.result__chrome{
  background:#e9ebef;
  height:30px;
  display:flex; align-items:center; gap:6px;
  padding:0 11px;
}
.result__dots{ display:flex; gap:5px; }
.result__dots i{ width:7px; height:7px; border-radius:50%; }
.result__dots i:nth-child(1){ background:#ff5f57; }
.result__dots i:nth-child(2){ background:#febc2e; }
.result__dots i:nth-child(3){ background:#28c840; }
.result__tab{
  display:flex; align-items:center; gap:5px;
  background:var(--white);
  border-radius:6px 6px 0 0;
  padding:4px 8px; margin-bottom:-6px;
  font-size:8.4px; color:#3c4043; letter-spacing:-.02em;
  white-space:nowrap;
}
.result__tab .x{ font-size:9px; color:#80868b; padding:1px 2px; line-height:1; }
.result__newtab{ font-size:13px; color:#5f6368; line-height:1; }
.result__addr{
  display:flex; align-items:center; gap:8px;
  height:26px;
  padding:0 11px;
  background:var(--white);
  border-bottom:.7px solid #dbdcdd;
}
.result__addr .nav{ display:flex; align-items:center; gap:8px; color:#5f6368; }
.result__addr .nav svg{ width:9px; height:9px; }
.result__addr .url{
  display:flex; align-items:center; gap:5px;
  background:#f0f3f4; border-radius:9px;
  height:18px; padding:0 8px; flex:1 1 0; min-width:0;
  font-size:9px; color:#202124;
}
.result__cursor{
  position:absolute; right:250px; top:48px;   /* matches cursorNav rest frame; rests just below the back arrow */
  width:18px; height:18px;
}
.result__body{ background:var(--white); height:100%; }

/* outcome status pucks on the branch */
.puck{
  width:32px; height:32px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  box-shadow:0 1px 2px rgba(16,24,40,.12);
}
.puck svg{ width:18px; height:18px; fill:#fff; }
.puck.ok  { left:874px; top:204px; background:var(--green); }
.puck.no  { left:874px; top:393px; background:var(--red); }

/* ===================================================================
   RESPONSIVE — scale the fixed-coordinate stage down on narrow screens.
   We swap to a vertical, simplified read below the breakpoint so the
   intricate mockups don't shrink into mush.
   =================================================================== */
@media (max-width:1080px){
  /* let the panel keep its ratio; the .scaler transform is set in JS
     so child px stay locked to design px. */
}

/* ===================================================================
   ANIMATIONS
   Reveal: blocks start hidden + nudged, then settle when .panel gets
   .is-in (added by IntersectionObserver). Stagger via --d (set from
   data-step in JS). Ambient loops run continuously once visible.
   =================================================================== */

/* --- reveal: cards / eyebrows slide+fade up --- */
.reveal{
  opacity:0;
  transform:translateY(14px);
  transition:opacity .55s ease, transform .55s cubic-bezier(.22,.61,.36,1);
  transition-delay:var(--d, 0s);
}
.panel.is-in .reveal{ opacity:1; transform:none; }

/* pucks pop with a little overshoot instead of a slide */
.reveal.pop{ transform:scale(.4); transform-origin:center; }
.panel.is-in .reveal.pop{
  transform:scale(1);
  transition:opacity .4s ease, transform .5s cubic-bezier(.34,1.56,.64,1);
  transition-delay:var(--d, 0s);
}

/* --- reveal: connector wires draw themselves ---
   All wire draw state (dasharray / dashoffset / transition) is driven INLINE
   by JS in the right order: prime hidden with NO transition -> commit via
   reflow -> attach transition + zero offset to animate the draw. Doing the
   prime+draw in one pass made the line paint instantly, so the steps are split
   across frames.
   Wires start `visibility:hidden` so they don't flash fully-drawn on the first
   paint (the script runs at end of <body>, after the SVG's first paint). JS
   makes them visible only after priming the dash to the hidden state. */
.wire{ visibility:hidden; }

/* --- ambient: waveform bars bounce --- */
.waveform i{ transform-origin:center; }
:is(.panel,.m-slide).is-in .waveform i{
  animation:wave 1s ease-in-out infinite;
  animation-delay:var(--wbd, 0s);
}
@keyframes wave{
  0%,100%{ transform:scaleY(.55); }
  50%    { transform:scaleY(1.25); }
}

/* --- ambient: 'Call in progress' status dot pulses --- */
:is(.panel,.m-slide).is-in .badge .dot{
  animation:livepulse 1.6s ease-in-out infinite;
}
@keyframes livepulse{
  0%,100%{ box-shadow:0 0 0 0 rgba(2,122,72,.45); }
  70%    { box-shadow:0 0 0 5px rgba(2,122,72,0); }
}

/* --- ambient loop: cursor clicks through to Google ---
   One 6s timeline, all pieces synced:
   0-25%  cursor glides halfway up from rest toward the address area
   27-31% click: cursor dips + a radar ripple fires at the click point
   33%    tab title -> 'Google', URL -> 'google.com'
   33-80% holds on Google
   80-100% reverts to the firm site, cursor drifts back -> loop
   Target = the BACK arrow (left of the address bar), card-local ~x15/y43.
   Card is 285px wide, so the arrow sits at right:270/top:43; the cursor SVG
   tip is at its box top-left, so click frame right:268/top:40 lands the tip
   on the arrow. Rest sits just below at right:262/top:48. */
:is(.panel,.m-slide).is-in .result__cursor{
  animation:cursorNav 6s ease-in-out infinite;
}
@keyframes cursorNav{
  0%      { right:250px; top:48px; transform:scale(1); }
  25%     { right:256px; top:40px; transform:scale(1); }
  28%     { right:256px; top:40px; transform:scale(.82); }  /* click down */
  32%     { right:256px; top:40px; transform:scale(1); }    /* click up   */
  78%     { right:256px; top:40px; transform:scale(1); }
  100%    { right:250px; top:48px; transform:scale(1); }
}

/* radar ripple: a ring that expands + fades at the click point, pinned to the
   click target so it fires right under the cursor tip */
.result__click{
  position:absolute;
  /* centred on the cursor TIP at the click moment.
     Cursor click frame right:268/top:40; SVG 18x18 with the tip ~3.75/2.25
     into the box, so the tip sits ~right:264/top:42. Ring is 14px, so offset
     by 7 to centre it on the back arrow. */
  right:259px; top:35px;
  width:14px; height:14px;
  border-radius:50%;
  border:1.5px solid var(--pink);
  opacity:0;
  pointer-events:none;
  transform:scale(.2);
}
:is(.panel,.m-slide).is-in .result__click{
  animation:radar 6s ease-out infinite;
}
@keyframes radar{
  0%,27%  { opacity:0; transform:scale(.2); }
  29%     { opacity:.9; transform:scale(.2); }
  40%     { opacity:0; transform:scale(2.4); }
  100%    { opacity:0; transform:scale(2.4); }
}

/* tab title + URL crossfade firm <-> Google, synced to the same 6s click loop.
   Both labels share one grid cell so they overlap and fade between each other. */
.result__tabtitle, .result__urltext{
  display:inline-grid;
  grid-template-areas:"stack";
}
.result__tabtitle > span, .result__urltext > span{
  grid-area:stack;
  white-space:nowrap;
}
.t-google, .u-google{ opacity:0; }   /* hidden until navigation */
:is(.panel,.m-slide).is-in .t-firm  { animation:fadeFirm   6s ease-in-out infinite; }
:is(.panel,.m-slide).is-in .t-google{ animation:fadeGoogle 6s ease-in-out infinite; }
:is(.panel,.m-slide).is-in .u-firm  { animation:fadeFirm   6s ease-in-out infinite; }
:is(.panel,.m-slide).is-in .u-google{ animation:fadeGoogle 6s ease-in-out infinite; }
@keyframes fadeFirm{   /* firm site: visible, hides while on Google */
  0%,31%  { opacity:1; }
  35%,78% { opacity:0; }
  82%,100%{ opacity:1; }
}
@keyframes fadeGoogle{ /* Google: hidden, shows after the click */
  0%,31%  { opacity:0; }
  35%,78% { opacity:1; }
  82%,100%{ opacity:0; }
}

/* ===================================================================
   MOBILE CAROUSEL
   Default: hidden (desktop uses the scaled .panel). Below 760px the
   desktop panel is hidden and this scroll-snap carousel takes over.
   Cloned cards keep their original classes so they render identically;
   the clones live inside .m-stage which neutralises the desktop absolute
   positioning and lays them out in normal flow.
   =================================================================== */
.m-carousel{ display:none; }

@media (max-width:759px){
  .tlm-referral-flow .panel{ display:none; }                 /* hide the desktop graphic */
  .tlm-referral-flow__subtitle{ display:none; }              /* desktop-only subtitle */
  .tlm-referral-flow__byline{ display:none; }                /* desktop-only byline */

  /* The carousel carries its own cream box on mobile, so strip the
     wrapper's background/padding to avoid a doubled-up panel. The desktop
     48px top margin is dropped too — the parent __inner gap alone sets the
     space above the carousel (matched to the section's bottom rhythm). */
  .tlm-referral-flow{
    background:none;
    padding:0;
    border-radius:0;
    margin-top:0;
  }

  .m-carousel{
    display:block;
    width:100%;
    background:var(--bg);
    border-radius:16px;
    padding:28px 0 18px;
    overflow:hidden;
  }
  .m-track{
    display:flex;
    align-items:flex-start;   /* don't stretch slides; let each size to content */
    /* A horizontal scroll container (overflow-x:auto) forces overflow-y to clip,
       so the track must be tall enough to contain the tallest slide or it cuts
       the card's bottom. min-height covers eyebrow + 392px card + pad + shadow. */
    min-height:472px;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
  }
  .m-track::-webkit-scrollbar{ display:none; }

  .m-slide{
    flex:0 0 100%;
    width:100%;
    scroll-snap-align:center;
    display:flex;
    flex-direction:column;
    align-items:center;
    padding:0 24px 8px;       /* bottom pad so the card's shadow has room */
    /* no overflow clip: cards must show their full height (rounded bottom +
       shadow). Horizontal containment is handled by the track's overflow-x. */
  }

  /* eyebrow above each slide (reuses desktop type via local rules) */
  .m-eyebrow{
    display:flex; align-items:center; gap:9px;
    font-family:"Proxima Nova", var(--sans);
    font-weight:700; font-size:15px; letter-spacing:.085em;
    text-transform:uppercase; color:var(--navy);
    margin-bottom:22px;
  }
  .m-eyebrow .dot{ width:8px; height:8px; border-radius:50%; background:var(--navy); flex:none; margin-top:-1px; }

  /* text-only intro / summary slides: centred copy filling the same vertical
     space the eyebrow + 392px card occupy on the other slides, so the dots and
     swipe rhythm stay even. Font sizes mirror the desktop subtitle/byline. */
  .m-slide--text{ justify-content:center; }
  .m-textcard{
    width:var(--m-card-w);
    min-height:432px;            /* eyebrow row (~40px) + 392px card on other slides */
    display:flex; align-items:center; justify-content:center;
    text-align:center;
    padding:0 8px;
  }
  .m-textcard p{ margin:0; }
  .m-textcard--head p{
    font-family:var(--serif);
    font-weight:500;
    font-size:24px;
    line-height:1.2;
    letter-spacing:-.01em;
    color:var(--navy);
  }
  .m-textcard--byline p{
    font-family:var(--sans);
    font-size:16px;
    line-height:1.6;
    color:var(--muted);
  }
  .m-textcard--byline strong{ font-weight:700; }
  .m-eyebrow .hl{ color:var(--pink); }

  /* the clone host: a FIXED card width (shared with the connectors via
     --m-card-w) so the wires anchor to the real card edge, not a guessed
     320px. Clamped so it never exceeds the slide's content width. */
  .m-slide{ --m-card-w:min(320px, 100% - 48px); }
  .m-stage{ position:relative; width:var(--m-card-w); max-width:none; margin:0 auto; }

  /* neutralise desktop absolute placement on the cloned cards.
     Keep position:relative (not static) so .evidence-pop / .result__cursor
     etc. still anchor to their card as the offset parent. */
  .m-stage .card-ask,
  .m-stage .card-look,
  .m-stage .col-contact{
    position:relative !important;
    left:auto !important; top:auto !important;
    width:100% !important; height:auto !important;
    transform:none !important;
  }
  /* ASK + LOOK share one fixed height on mobile. The LOOK card's browser +
     evidence-pop are absolutely positioned (nothing in normal flow), so the
     card needs an explicit height tall enough to show the whole mockup without
     clipping. ASK is matched to it so the two slides line up. */
  .m-stage .card-ask,
  .m-stage .card-look{ height:392px !important; }
  /* CONTACT slide: occupy the same 392px height as ASK/LOOK, with the call
     card pinned to the top and the browser result card pinned to the bottom so
     its bottom edge lines up with the other two slides' cards. */
  .m-stage .col-contact{
    display:flex; flex-direction:column; justify-content:space-between;
    height:392px !important;
  }
  .m-stage .call{ height:auto; }        /* sizes to content, sits at the top */
  .m-stage .result{ height:158px; }     /* keep the browser card proportion */
  /* desktop branch pucks (cloned inside the card) stay hidden; the mobile
     carousel uses its own .m-puck elements positioned per slide */
  .m-stage .puck{ display:none; }

  /* the slide is the positioning context for the edge-bleeding connectors. */
  .m-slide{ position:relative; }

  /* Connector geometry. Vertical positions = measured card-row centres
     (relative to slide top): ASK/LOOK card centre = 233px, call card row
     = 114px, result card row = 350px. Horizontal anchors come from
     --m-edge-l / --m-edge-r (derived from the real card width below), so
     the wires meet the card edge at any viewport. Lines are 1.6px bars;
     SVG arrowheads point INTO the cards. */

  /* Card edges, derived from the real (variable) card width so the wires
     always meet the card no matter the viewport. */
  .m-slide{ --m-edge-r:calc(50% + var(--m-card-w) / 2); --m-edge-l:calc(50% - var(--m-card-w) / 2); }

  .m-line{ position:absolute; height:1.6px; background:var(--line); transform:translateY(-50%); z-index:1; }
  .m-head{ position:absolute; width:12px; height:16px; transform:translateY(-50%); z-index:2; }

  /* CARD 1 (ASK): right side = plain line from the card edge out to the page edge, NO head */
  .m-line--right{ top:233px; left:var(--m-edge-r); right:0; }

  /* CARD 2 (LOOK):
     left  = line from the page edge up to the card + arrowhead pointing IN
     right = branch: two lines splitting from the card edge out to the page edge */
  .m-line--left{ top:233px; left:0; right:calc(100% - var(--m-edge-l) + 4px); }
  .m-head--in-left{ top:233px; right:calc(100% - var(--m-edge-l) + 1px); }   /* tip kisses the card's left edge */
  .m-branch span{
    position:absolute; height:1.6px; background:var(--line);
    left:var(--m-edge-r);        /* card's right edge */
    right:0;                      /* page edge */
    transform:translateY(-50%);
    z-index:1;
  }
  .m-branch__top{ top:114px; }   /* aligns with card 3's call-card row  */
  .m-branch__bot{ top:350px; }   /* aligns with card 3's result-card row */

  /* cursor + radar click target is the BACK arrow at the card's LEFT edge,
     positioned via `right:` against the 285px desktop card. The mobile clone
     card is up to 320px wide, so the same left-edge point needs a larger
     `right` offset: add (m-card-w - 285) via margin-right so the keyframes stay
     shared. */
  .m-slide .result__cursor,
  .m-slide .result__click{ margin-right:calc(var(--m-card-w) - 285px + 47px); }

  /* CARD 3 (CONTACT): two lines from the left page edge into each card, with
     arrowheads at the card's left edge. Rows match the call/result centres. */
  .m-line--c3-top{ top:114px; left:0; right:calc(100% - var(--m-edge-l) + 4px); }
  .m-line--c3-bot{ top:350px; left:0; right:calc(100% - var(--m-edge-l) + 4px); }
  .m-head--c3-top{ top:114px; right:calc(100% - var(--m-edge-l) + 1px); }
  .m-head--c3-bot{ top:350px; right:calc(100% - var(--m-edge-l) + 1px); }
  /* evidence-pop is absolutely placed over the desktop browser; keep it
     anchored to the cloned card (its own .card-look is the offset parent) */

  /* dots */
  .m-dots{
    display:flex; justify-content:center; gap:8px;
    margin-top:24px;
  }
  .m-dot{
    width:8px; height:8px; border-radius:50%; border:0; padding:0;
    background:var(--line); cursor:pointer; transition:background .25s, width .25s;
  }
  .m-dot.is-active{ background:var(--pink); width:20px; border-radius:4px; }

  /* swipe hint */
  .m-hint{
    text-align:center; margin-top:12px;
    font-size:12px; color:var(--muted); letter-spacing:.01em;
  }
  .m-hint span::after{ content:" \2192"; }
  .m-carousel.m-moved .m-hint{ opacity:0; transition:opacity .4s ease; }
}

/* reduced-motion: everything appears, nothing moves */
@media (prefers-reduced-motion: reduce){
  .reveal, .reveal.pop{ opacity:1 !important; transform:none !important; transition:none; }
  .wire{ stroke-dasharray:none; stroke-dashoffset:0; }
  .panel.is-in .waveform i,
  .panel.is-in .badge .dot,
  .panel.is-in .result__cursor,
  .panel.is-in .result__click,
  .panel.is-in .t-firm, .panel.is-in .t-google,
  .panel.is-in .u-firm, .panel.is-in .u-google{ animation:none; }
  .t-google, .u-google{ display:none; }   /* show only the firm site */
}
