/* ═══════════════════════════════════════════════════════════════════
   RAVEHQ MOTION-V2 — shared motion CSS
   Extracted + generalized from site/_hp_directions/01-pipeline-draws-itself.html
   (D-006 winner: "the pipeline draws itself"). Governs all motion sitewide
   per planning/DESIGN_CONTRACT_V2.md.

   Rules baked into this file (do not violate when composing pages):
   - Every animation is transform/opacity only. No layout properties.
   - prefers-reduced-motion fallbacks are OPACITY-ONLY overrides.
     NEVER add `transform:none !important` to a positioned SVG group —
     that snaps pipeline nodes out of their perimeter position and back
     into document-flow (0,0), which is the exact bug the winner fixed.
     Reduced motion means "skip the animation, keep the end state,"
     not "strip the base transform that places the element."
   - <880px: pipeline visuals hide, typography carries the hero.
   ═══════════════════════════════════════════════════════════════════ */

:root{
  /* tokens — VISUAL_CRAFT_CONTRACT v1, unchanged */
  --canvas:#FAF7F1;
  --surface:#FFFFFF;
  --surf-w:#F3EEE5;
  --ink:#121217;
  --ink-m:#4A4A5A;
  --ink-l:#646471; /* Golden Sweep Phase B (2026-07-03): was #7A7A8A, failed
    WCAG 4.5:1 on every background it's used against (402 sitewide
    findings, contrast_gate.py) — see assets/style.css's matching token
    for the full rationale. This file has a sibling copy at the other
    site/ vs site-v2/ assets/motion-v2.css path — keep BOTH copies
    byte-identical (M7 discipline: stale-copy root cause; this exact
    comment block must match verbatim in both, not just the token). */
  --line:#E4DECE;
  --gold:#B8892A;
  --gold-l:#E8C96A;
  --gold-bg:#FDF3DC;
  --navy:#0D1B35;
  --win:#117A4A;
  --loss:#C03020;

  /* motion timing scale — reused across every primitive below */
  --mv2-ease-out: cubic-bezier(.22,1,.36,1);
  --mv2-ease-inout: cubic-bezier(.45,0,.55,1);
  --mv2-stagger: 100ms;
  --mv2-fade-dur: .6s;
  --mv2-draw-dur: .6s;
}

/* ---------------------------------------------------------------------
   0. BASE RESET (scoped additions only — do not fight assets/style.css)
--------------------------------------------------------------------- */
.mv2-root{ overflow-x:hidden; }
/* Golden Sweep fix wave (2026-07-03): overflow-x:hidden on .mv2-root
   (= <body class="mv2-root">) does NOT reliably suppress document-level
   horizontal scroll, because document.scrollingElement resolves to
   <html> in this document (confirmed via
   document.scrollingElement.tagName === 'HTML'), and <html> itself had
   no overflow-x restriction. This is a known browser gotcha: the
   overflow property only stops the SCROLLING ELEMENT's own scroll —
   setting it on body is a no-op for scroll suppression when html is
   the one actually scrolling. Confirmed by direct repro: pages with a
   genuinely wide descendant (e.g. insights/from-search-to-answer-
   engine.html's .compare-table, min-width:420px inside a scrollable
   wrapper) showed a REAL horizontal scroll (window.scrollTo(1000,0)
   actually moved scrollX to 103) despite .mv2-root's overflow-x:hidden
   being correctly applied and computed — because it was applied to the
   wrong element. Setting it on html as well closes the gap: verified
   this single-line addition alone drops document.scrollingElement.
   scrollWidth from 463 to 360 (exact viewport width, zero overflow) on
   the affected page, with no other page-local changes. This is the
   true root cause behind the §8.1 "zero horizontal overflow" floor,
   more fundamental than the previous per-page investigation (Root
   cause D) suggested — the table-wrap fix in that page was still
   correct/necessary (overflow-x:auto + max-width:100% properly
   contains the table so it's touch-scrollable instead of just spilling
   sideways) but this html-level fix is what actually stops the
   overflow from reaching the page's own scroll region regardless of
   which specific element on a given page might overflow in the future. */
html{ overflow-x:hidden; }

/* ---------------------------------------------------------------------
   1. ENTRANCE STAGGER — fade-up
   Usage: add [data-mv2="fade-up"] to any element inside a
   [data-mv2-group] container. motion-v2.js walks children in DOM order
   and staggers them by --mv2-stagger. Works with or without JS present
   because the CSS end-state is opacity:1 by default — JS only ADDS the
   animated entrance; a JS failure never hides content (see the class
   toggle model in section 6).
--------------------------------------------------------------------- */
[data-mv2="fade-up"]{
  opacity:1;
  transform:none;
}
/* JS arms the entrance by adding .mv2-armed before elements enter the
   viewport / before the load timeline fires. Only THEN do we hide them,
   so a script that never runs (blocked, slow 3G, ad-blocker) can never
   leave content invisible. */
.mv2-armed[data-mv2="fade-up"]{
  opacity:0;
  transform:translateY(16px);
  transition:opacity var(--mv2-fade-dur) var(--mv2-ease-out),
             transform var(--mv2-fade-dur) var(--mv2-ease-out);
}
.mv2-armed[data-mv2="fade-up"].mv2-in{
  opacity:1;
  transform:translateY(0);
}

/* ---------------------------------------------------------------------
   2. SVG PATH DRAW-ON SYSTEM
   Usage: <path class="mv2-flow-line" data-mv2-draw> for the base line,
   <path class="mv2-flow-line-active" data-mv2-pulse-path="segId"> for
   the gold traveling overlay on the SAME path geometry, and
   <g class="mv2-node" data-mv2-node> for each destination node.
   motion-v2.js measures getTotalLength(), sets stroke-dasharray /
   stroke-dashoffset, then draws to 0 and fades the destination node in.
--------------------------------------------------------------------- */
.mv2-flow-line{
  fill:none;
  stroke:var(--line);
  stroke-width:1.6;
}
.mv2-flow-line-active{
  fill:none;
  stroke:var(--gold);
  stroke-width:2;
  stroke-linecap:round;
}
.mv2-pulse-dot{
  fill:var(--gold);
  filter:drop-shadow(0 0 6px rgba(184,137,42,0.6));
}

/* Node starts invisible ONLY once JS has armed it (see .mv2-armed
   pattern above) — same fail-open guarantee as the fade-up primitive. */
.mv2-node{ opacity:1; }
.mv2-armed.mv2-node{ opacity:0; }
.mv2-armed.mv2-node.mv2-in{ opacity:1; transition:opacity .5s var(--mv2-ease-out); }

.mv2-node-ring{
  fill:var(--canvas);
  stroke:var(--navy);
  stroke-width:1.6;
}
.mv2-node-ring-active{
  fill:var(--gold-bg);
  stroke:var(--gold);
}
.mv2-node-icon{
  stroke:var(--navy);
  stroke-width:1.6;
  fill:none;
  stroke-linecap:round;
  stroke-linejoin:round;
}
.mv2-node-icon-accent{
  stroke:var(--gold);
  stroke-width:1.8;
  fill:none;
  stroke-linecap:round;
}
.mv2-node-label{
  font-family:'Switzer',sans-serif;
  font-weight:700;
  font-size:11px;
  letter-spacing:0.08em;
  text-transform:uppercase;
  fill:var(--ink-l);
}

/* ---------------------------------------------------------------------
   3. CONTENT-EXCLUSION-ZONE PIPELINE LAYOUT
   THE RULE (locked, from D-006 repair): the SVG pipeline is a full-bleed
   background layer (position:absolute, inset:0, z-index under content).
   Nodes live ONLY on the perimeter of the viewBox — a top strip and a
   bottom strip. The vertical center third of the viewBox is a protected
   safe-zone for the headline/copy column and must stay clear of any
   node, label, or connector that crosses through it horizontally.
   Verified reference points from the winner (1600x1000 viewBox):
     node4  -> translate(410,940)   (bottom strip)
     node5  -> translate(1195,905)  (bottom strip)
   Connectors that must travel top-to-bottom hug the far left/right
   edges (x<80 or x>1520 in a 1600-wide viewBox), never crossing the
   vertical center. Any new page composing this pattern MUST keep its
   node coordinates inside the same perimeter bands relative to its own
   viewBox — do not place a node inside the middle band.
--------------------------------------------------------------------- */
.mv2-pipeline-stage{
  position:absolute;
  inset:0;
  z-index:1;
  display:flex;
  align-items:center;
  justify-content:center;
  pointer-events:none;
}
.mv2-pipeline-svg{
  width:min(1600px,150vw);
  height:auto;
  max-height:92vh;
  opacity:0.85;
}
/* The safe-zone rect is authoring guidance, not a runtime element —
   draw it as a temporary <rect data-mv2-safezone> at 25%-75% height
   while composing a new pipeline layout, then delete it before ship.
   motion-v2.js will warn in the console if a node's y-coordinate (or
   x-coordinate translation) falls inside that band on page load. */
.mv2-safezone-debug{
  fill:rgba(192,48,32,0.06);
  stroke:rgba(192,48,32,0.4);
  stroke-dasharray:6 6;
}

/* Content column sits above the pipeline layer, unaffected by it */
.mv2-content-col{
  position:relative;
  z-index:2;
}

/* ---------------------------------------------------------------------
   4. COUNT-UP NUMBERS
   Usage: <span data-mv2-count data-mv2-count-to="78" data-mv2-count-from="62">
   motion-v2.js tweens textContent from the "from" value (default 0) to
   "to" over --mv2-count-dur (default 1.1s). Purely a JS text mutation —
   no layout-affecting CSS needed, but we still gate the trigger via
   IntersectionObserver (section 5) so it fires once, in view.
--------------------------------------------------------------------- */
[data-mv2-count]{ font-variant-numeric:tabular-nums; }

/* ---------------------------------------------------------------------
   5. SCROLL-REVEAL (IntersectionObserver-driven)
   Usage: add [data-mv2-reveal] to a container. motion-v2.js observes it;
   on first intersection it adds .mv2-in to the container AND to every
   descendant carrying [data-mv2="fade-up"], honoring per-child stagger
   via each child's DOM order (index * --mv2-stagger).
--------------------------------------------------------------------- */
[data-mv2-reveal]{ opacity:1; }
.mv2-armed[data-mv2-reveal]{
  opacity:0;
  transform:translateY(18px);
  transition:opacity .7s var(--mv2-ease-out), transform .7s var(--mv2-ease-out);
}
.mv2-armed[data-mv2-reveal].mv2-in{
  opacity:1;
  transform:translateY(0);
}

/* ---------------------------------------------------------------------
   6. SHIMMER SWEEP (scanning-moment primitive, from VISUAL_CRAFT_CONTRACT
   Rule 6) — opt-in via .mv2-shimmer-active, one-shot, CSS-only.
--------------------------------------------------------------------- */
@keyframes mv2-shimmer{
  0%{transform:translateX(-100%);}
  100%{transform:translateX(250%);}
}
.mv2-shimmer-surface{ position:relative; overflow:hidden; }
.mv2-shimmer-surface::after{
  content:'';
  position:absolute; top:0; left:0; width:40%; height:100%;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.55),transparent);
  transform:translateX(-100%); pointer-events:none; opacity:0;
}
.mv2-shimmer-surface.mv2-shimmer-active::after{
  opacity:1;
  animation:mv2-shimmer 1.1s ease-in-out 1;
}

/* ---------------------------------------------------------------------
   7. CTA HOVER LIFT (Rule 6 — subtle and premium)
--------------------------------------------------------------------- */
.mv2-hover-lift{ transition:transform .15s ease, box-shadow .15s ease; }
.mv2-hover-lift:hover{ transform:translateY(-1px); }

/* ---------------------------------------------------------------------
   8. MOBILE RULE (<880px): pipeline cinema hides, typography carries
--------------------------------------------------------------------- */
@media (max-width:880px){
  .mv2-pipeline-stage{ display:none; }
  .mv2-hide-mobile{ display:none !important; }
}

/* ---------------------------------------------------------------------
   9. REDUCED-MOTION FALLBACKS — opacity-only, per the hard rule above.
   Every animated primitive gets its end-state locked in and its
   animation/transition killed. transform is NEVER reset to none on a
   .mv2-node or any element carrying a positional SVG transform.
--------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }

  .mv2-armed[data-mv2="fade-up"],
  .mv2-armed[data-mv2-reveal]{
    opacity:1 !important;
    transition:none !important;
    /* transform intentionally untouched for SVG-positioned elements;
       for plain HTML fade-up blocks translateY(0) is harmless and
       matches the settled state, so it is safe to zero here. */
  }
  [data-mv2="fade-up"]:not(g):not(svg *){ transform:none !important; }

  .mv2-armed.mv2-node{
    opacity:1 !important;
    transition:none !important;
    /* transform NOT reset — see file header + section 3 rule. The
       node's translate(x,y) is its layout position, not an animation. */
  }

  .mv2-pulse-dot{ display:none; }
  .mv2-flow-line-active{
    stroke-dasharray:none !important;
    stroke-dashoffset:0 !important;
    opacity:0.9;
  }
  .mv2-shimmer-surface.mv2-shimmer-active::after{ animation:none !important; opacity:0 !important; }
  .mv2-scroll-cue .mv2-scroll-line::after{ animation:none !important; }
}

/* ═══════════════════════════════════════════════════════════════════
   10. MOBILE LAYER — Contract v2.1 (locked 2026-07-03)
   Class-level mobile primitives per DESIGN_CONTRACT_V2.md §8. Pages
   compose these classes; they do NOT reimplement the pattern inline
   (page-local reinvention is a contract violation — see §8.8).

   What lives here:
     10.1  Compact vertical mini-pipeline (.mv2-vpipe) — the mobile
           equivalent of the full desktop pipeline cinema. Motion
           survives mobile per L-050: this is NOT a hide-and-replace-
           with-nothing pattern, it's a genuine adapted equivalent.
     10.2  Mobile type floors — headline clamp floor, body floor,
           input floor (iOS zoom-jump guard).
     10.3  Mobile spacing clamps for shared primitives.
     10.4  Collapse rules — desktop-only artifacts close their space
           instead of leaving a void (§8.6).
     10.5  Tap target floor — nav drawer + generic interactive floor.
   ═══════════════════════════════════════════════════════════════════ */

/* ---------------------------------------------------------------------
   10.1 COMPACT VERTICAL MINI-PIPELINE
   Usage:
     <div class="mv2-vpipe" data-mv2-vpipe data-mv2-reveal>
       <div class="mv2-vpipe-line" data-mv2-vpipe-line></div>
       <div class="mv2-vpipe-node" data-mv2="fade-up">
         <span class="mv2-vpipe-icon">...svg...</span>
         <span class="mv2-vpipe-label">Customer visits</span>
       </div>
       ... 2 more nodes (3 total — compact, not the full 6-7 node story) ...
     </div>
   The connecting line draws top-to-bottom via the SAME stroke-dasharray/
   stroke-dashoffset mechanic as the desktop pipeline (motion-v2.js
   §10 initVPipe), just re-oriented to a vertical SVG line instead of a
   full-bleed background scene. Hidden above 880px (desktop uses the
   full pipeline instead) and shown only below it — the two are mutually
   exclusive, never both mounted visibly at once.
--------------------------------------------------------------------- */
.mv2-vpipe{
  display:none; /* shown only under the 880px breakpoint, see below */
  position:relative;
  flex-direction:column;
  gap:28px;
  max-width:100%;
  box-sizing:border-box;
}
.mv2-vpipe-line-svg{
  position:absolute;
  top:6px; bottom:6px; left:0;
  width:44px; /* matches the node's reserved icon-gutter width exactly,
    so the line always centers under the icon column regardless of the
    node's own padding-box math (no negative-margin coupling). */
  overflow:visible;
  pointer-events:none;
}
.mv2-vpipe-line-svg path{
  fill:none;
  stroke:var(--line);
  stroke-width:1.6;
}
.mv2-vpipe-line-svg path.mv2-vpipe-line-active{
  stroke:var(--gold);
  stroke-width:2;
  stroke-linecap:round;
}
.mv2-vpipe-node{
  position:relative;
  display:flex;
  align-items:center;
  min-height:44px; /* tap-target-adjacent rhythm even though these are non-interactive */
  padding-left:44px; /* reserves the icon-gutter INSIDE the node's own
    box (box-sizing:border-box), so the icon can be absolutely
    positioned at left:0 without any negative-margin math that could
    escape the container's edge (§8.1 zero-overflow discipline). */
  box-sizing:border-box;
}
.mv2-vpipe-icon{
  position:absolute;
  left:0; top:50%;
  transform:translateY(-50%);
  width:32px; height:32px;
  border-radius:50%;
  background:var(--canvas);
  border:1.6px solid var(--navy);
  display:flex; align-items:center; justify-content:center;
}
.mv2-vpipe-node.mv2-vpipe-node-active .mv2-vpipe-icon{
  background:var(--gold-bg);
  border-color:var(--gold);
}
.mv2-vpipe-icon svg{ width:16px; height:16px; stroke:var(--navy); fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.mv2-vpipe-node.mv2-vpipe-node-active .mv2-vpipe-icon svg{ stroke:var(--gold); }
.mv2-vpipe-label{
  font-family:'Switzer',sans-serif;
  font-weight:600;
  font-size:15px;
  line-height:1.3;
  color:var(--ink);
}
.mv2-vpipe-node-active .mv2-vpipe-label{ font-weight:700; }

/* Count-up / reveal equivalent (§8.7 option 2) — a staggered card
   sequence with a leading stat, for pages where a connector line is
   visually redundant with existing card rhythm. No new classes needed:
   compose [data-mv2-reveal] + [data-mv2="fade-up"] + [data-mv2-count]
   (all already shipped in §1/§4/§5 above) inside a single-column mobile
   stack. This comment documents the pattern so it isn't independently
   reinvented per §8.8 — the primitives already exist, mobile usage is
   just "use them in a single column below 880px instead of omitting
   motion entirely." */

@media (max-width:880px){
  .mv2-vpipe{ display:flex; }
}

/* ---------------------------------------------------------------------
   10.2 MOBILE TYPE FLOORS (§8.2 / §8.3)
   These are FLOOR guards, not replacements for a page's own clamp()
   scale. Pages should already be using clamp() with a sane vw
   coefficient (per VISUAL_CRAFT_CONTRACT + existing sitewide pattern);
   this block is the safety net for any element that isn't, or whose
   coefficient starves below the contract floor at 360px.
--------------------------------------------------------------------- */
/* CASCADE-FIGHT FIX (2026-07-03, Golden Sweep fix wave): every v2 page's
   own <style> block loads AFTER this file (confirmed: motion-v2.css link
   sits in <head> around line 16-17 on all 32 pages; the page's own
   <style> block starts well after that, e.g. line 144+ on index.html).
   A same-specificity rule that loads LATER wins ties regardless of file
   load order — so a bare page-local `.nav-links a{font-size:14.5px}`
   silently beat the old floor's `body,p,li{...}` selector (lower
   specificity) AND would have beaten an equal-specificity rewrite too,
   purely on source order. `!important` is the only reliable way to
   guarantee this shared floor wins regardless of a page's own specificity
   or position in the cascade — matches the precedent already set below
   on the input floor for the exact same reason. */
@media (max-width:414px){
  /* Headline floor: never below 32px, whole-word wrap only. Applied to
     the common heading selectors used across v2 pages; page-specific
     h1 classes should still be authored with clamp() directly — this
     is the backstop, not the primary mechanism. */
  h1, .display-h1, .rh2-hero-content h1{
    font-size: max(32px, 1em) !important; /* if the element's own clamp()
      already exceeds 32px this is a no-op; if a vw-driven clamp starved
      below 32px at this width, this floor rescues it without touching
      the page's declared clamp() call site. */
    overflow-wrap: normal !important;
    word-break: normal !important;
    hyphens: none !important; /* no mid-word breaks — whole-word wrap only per §8.2 */
  }

  /* Body / label floor: 16px minimum for anything meant to be read as
     copy. Buttons/eyebrows with deliberately smaller tracked-caps
     labels are exempted via .mv2-label-sm (opt-in escape hatch, use
     sparingly and never for primary reading copy). Explicit sitewide
     nav/footer selectors added below — these are the confirmed repeat
     offenders (identical markup copy-pasted across all 32 pages) that a
     bare `body,p,li` floor cannot reach because they're inside `<a>`
     tags, which the original floor selector list didn't cover at all.
     `div` and `span` are included here too: the detector surfaced real
     paragraph-length reading copy sitting in bare `<div>`/`<span>` tags
     sitewide (e.g. get-started.html form-flow copy, personas.html body
     text) — §8.3 says "body copy... UI text" not "only <p> tags", so
     these get the same 16px floor as everything else. */
  body, p, li, a, div, span, .mv2-body-text,
  .nav-links a, .nav-links a.nav-cta, .nav-drawer a,
  .footer-links a, .footer-copy, .footer-logo,
  input, select, textarea, button{
    font-size: max(16px, 1em) !important;
  }

  /* Micro-label exception class (§8.3's own "intentional fine print"
     carve-out): the widget-mockup ecosystem (leak-calculator gauges,
     RaveScore mini-charts, how-it-works pipeline mockups — verticals/*,
     how-it-works.html, index.html audit-widget) authors ~100 distinct
     short eyebrow/badge/gauge-label classes (vt-*, vt2-*, hiw2-*,
     rh2-*-label/-eyebrow/-caption/-badge, sec-label-gold, etc.) that are
     genuinely decorative UI chrome, not reading copy — raising them to
     16px would blow out compact widget layouts that were sized around
     an intentionally small label scale. These get a 14px floor instead
     (the numeric minimum §8.3's fine-print exception allows) rather
     than the full 16px body floor. NAMED CONTRAST CAVEAT (out of this
     section's scope, flagged for VISUAL_CRAFT_CONTRACT.md's color
     domain — not silently claimed as passing): computed WCAG ratios
     show `--gold` on `--canvas` = 2.95:1 and `--ink-l` on `--canvas`/
     `--surface` = 3.94:1/4.22:1 — ALL fail AA-normal (4.5:1), and
     `--gold` on `--canvas` fails even AA-large (3.0:1). Any class below
     using those two color tokens (`.sec-label-gold` confirmed on
     `--gold`; several `--ink-l`-toned labels) is a color problem, not a
     size problem — §8.3 only owns type floors, so this rule fixes the
     size half and NAMES the unresolved color half rather than
     over-claiming full compliance. Opt a class into the 14px floor by
     adding `.mv2-label-sm` to its markup; classes NOT opted in still
     get the full 16px floor above (safe default — opt-out required,
     not opt-in-required, so nothing silently stays tiny by omission). */
  .mv2-label-sm{ font-size: max(14px, 1em) !important; }
}

/* Input floor applies at EVERY mobile width, not just 414px-down, since
   iOS zoom-jump triggers on any focus event on a sub-16px field
   regardless of exact viewport — this is a Safari behavior, not a
   layout breakpoint concern. 16px is the documented Safari threshold. */
@media (max-width:880px){
  input, select, textarea{
    font-size: max(16px, 1em) !important;
  }
}

/* ---------------------------------------------------------------------
   10.3 MOBILE SPACING CLAMPS FOR SHARED PRIMITIVES (§8.5)
   Compressed single-column rhythm below 880px for anything composing
   the shared motion-v2 primitives (fade-up groups, reveal sections,
   pipeline stage wrapper). Page-local grids still need their own
   grid-template-columns:1fr override (§8.6) — this block only handles
   the primitives this file owns.
--------------------------------------------------------------------- */
@media (max-width:880px){
  [data-mv2-group], [data-mv2-reveal]{
    padding-left:0; padding-right:0; /* let the page's own container
      padding govern horizontal rhythm; this file never introduces its
      own competing gutter that could combine with a page gutter and
      push total width past the 360px floor (§8.1). */
  }
  .mv2-vpipe{ margin-top:8px; margin-bottom:8px; }
}

/* ---------------------------------------------------------------------
   10.4 COLLAPSE RULES — desktop-only artifacts close their space (§8.6)
   The existing §8 mobile rule (.mv2-pipeline-stage{display:none} at
   880px) hides the VISUAL layer but a parent hero section keeping
   min-height:100vh around empty space is the violation this section
   guards against. .mv2-collapse-below-880 is an opt-in class pages add
   to the SIZED CONTAINER (not the inner visual) that should stop
   reserving space once its content is hidden.
--------------------------------------------------------------------- */
@media (max-width:880px){
  .mv2-collapse-below-880{
    min-height:0 !important;
    height:auto !important;
  }
  /* Generic guard for the pipeline hero pattern specifically: a section
     carrying BOTH .mv2-pipeline-stage as a child AND an inline/authored
     min-height:100vh on itself is exactly the void pattern §8.6 names.
     Pages should prefer padding-based height (clamp() top/bottom) over
     min-height:100vh for any section that also hides a background
     layer on mobile — this rule is the fallback for pages that haven't
     been rewritten that way yet. */
  .mv2-hero-autoheight{ min-height:0 !important; }
}

/* ---------------------------------------------------------------------
   10.5 TAP TARGET FLOOR (§8.4 / §8.9)
   Sitewide floor for the highest-risk known patterns (hamburger,
   nav-drawer links, desktop nav-links row, footer links, logo, mobile
   micro-buttons) plus a generic opt-in class for any other interactive
   element a page wants to guarantee against the 44px floor without
   hand-computing padding. This does not replace per-page audit (the
   detector in planning/MOBILE_DEFECTS_*.md checks every a/button on
   every page) — it fixes the patterns known to recur across all 32 v2
   pages via the shared nav/footer markup.

   2026-07-03 Golden Sweep fix wave: the original version of this block
   only covered .hamburger + .nav-drawer a. The detector's tap-target-
   small category (1787 baseline rows) showed the desktop .nav-links row
   (which the §8.4 audit had already flagged: "the hamburger button...
   currently under floor" but NOT flagged the sibling nav-links/footer-
   links row) and .footer-links a both compute an ~18-21px line-box with
   zero padding — font-size floor (§10.2) fixes readability but does
   nothing for hit-area, a completely separate CSS concern. Added below,
   using the same flex-row-as-hit-area pattern already proven on
   .nav-drawer a. All use !important for the same cascade-fight reason
   as §10.2 — a page-local .nav-links a{...} rule loading later in the
   page's own <style> block would otherwise still win ties. */
@media (max-width:900px){
  .hamburger{
    min-width:44px !important; min-height:44px !important;
    align-items:center !important; justify-content:center !important;
  }
}
.nav-drawer a{
  min-height:44px;
  display:flex; align-items:center; /* full-width flex row, not a bare
    inline link with only vertical padding — guarantees the entire row
    is the hit area, not just the text glyph height. */
}
@media (max-width:880px){
  /* Desktop .nav-links row is display:none below ~900px on every page
     (replaced by the drawer), but T3 pages reuse .nav-links even where
     the drawer pattern differs slightly, and some pages' breakpoint for
     hiding it sits at 900px vs 880px — apply below 880px to be safely
     inside every page's own hide-breakpoint, matching this file's
     existing 880px convention elsewhere in §10. */
  .nav-links a, .footer-links a{
    min-height:44px !important;
    min-width:44px !important; /* short single-word links ("About",
      "Terms") measured 43.14px wide with height-only sizing — a link's
      rendered width is driven by its own text content under inline-flex
      with no horizontal padding, so a 5-letter label can fall under the
      44px floor in WIDTH even once height passes. justify-content:center
      below keeps the (rare, short-label) case where min-width pads
      beyond the text's natural width from looking left-aligned/odd. */
    display:inline-flex !important; align-items:center !important;
    justify-content:center !important;
    padding-top:6px !important; padding-bottom:6px !important; /* visual
      breathing room so the enlarged hit area doesn't look like an
      unstyled oversized row — matches the ~44px total via font-size(16)
      + line-height + this padding rather than a bare min-height alone. */
  }
  .footer-links{
    row-gap:4px !important; /* the added a-padding above already supplies
      vertical rhythm; avoid double-spacing on top of flex-wrap's
      existing gap:20px (which still governs horizontal gap). */
  }
  a.logo{
    min-height:44px !important;
    display:inline-flex !important; align-items:center !important;
  }
  /* Cookie-consent banner buttons: markup is JS-injected at runtime by
     assets/main.js (not present in any page's static HTML source, so
     there's no page-local <style> block to edit even if that were in
     scope) with base styling from assets/style.css (also out of this
     fix wave's declared touch-scope: site-v2/**, motion-v2.css/js, and
     the two named planning files only). Fixed here instead — !important
     wins regardless of which file's rule loads in what order, so this
     is a legitimate in-scope fix path without touching style.css/
     main.js. Measured 93x40px / 96x40px (padding:10px 18px, font 13px)
     — under the 44px floor in height only. */
  .cookie-btn{
    min-height:44px !important;
    display:inline-flex !important; align-items:center !important; justify-content:center !important;
    box-sizing:border-box !important;
  }
  /* Breadcrumb links (6 insight-article pages: can-ai-see-your-business,
     from-search-to-answer-engine, how-to-respond-to-negative-reviews,
     the-quiet-tax-on-local-business, reviews-rank-flywheel, operating-
     leverage-of-managed-presence) — shared ".breadcrumb a" pattern,
     12px font (also rescued by the body-floor above) with zero padding.
     Fixed at the shared-file level rather than 6 individual page edits. */
  .breadcrumb a{
    min-height:44px !important; min-width:44px !important;
    display:inline-flex !important; align-items:center !important; justify-content:center !important;
  }
  /* compare.html uses its own differently-namespaced ".cmp2-breadcrumb"
     class instead of the shared ".breadcrumb" pattern above — same
     underlying bug (43x18px "Home" link), different selector, so it
     wasn't caught by the .breadcrumb rule. Added as its own selector
     rather than renaming compare.html's markup (surgical change only —
     the class name itself isn't broken, just missing the tap floor). */
  .cmp2-breadcrumb a{
    min-height:44px !important; min-width:44px !important;
    display:inline-flex !important; align-items:center !important; justify-content:center !important;
  }
}
.mv2-tap-floor{
  min-width:44px; min-height:44px;
  display:inline-flex; align-items:center; justify-content:center;
}
