/* GENERATED by scripts/build-wp-theme.mjs — the design-token layers, same order as the prototype styles.css. Edit tokens/*.css in the repo root, then: npm run wp-theme */
/* ====== tokens/fonts.css ================================================= */
/* Empire Vein & Vascular — Webfonts
   Brand face is Inter, self-hosted as one variable file (latin subset,
   weights 100–900) at ui_kits/website/assets/fonts/, shared with the
   landers. No third-party font CSS on the critical path; index.html
   preloads the file. Display headlines use Inter at 700–800 with tight
   tracking. The Caveat accent face is still fetched from Google Fonts
   (index.html) because it is used on one decorative line. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/inter-var-latin.woff2") format("woff2");
}

/* ====== tokens/colors.css ================================================ */
/* ============================================================
   Empire Vein & Vascular — COLOR TOKENS
   Source of truth: artifacts/empire-vein/src/index.css (brand purple
   #49136f, slate neutrals) + the design comps (purple→orange display
   gradient, deep-plum dark sections, amber CTA accent).
   ============================================================ */

:root {
  /* ---- Brand purple scale (primary identity) ---- */
  --ev-purple-25:  #faf8fe;  /* faintest lavender — row tints (code referenced it as a fallback for years) */
  --ev-purple-50:  #f5f1fa;  /* lavender page tint */
  --ev-purple-100: #ece3f4;
  --ev-purple-200: #d8c4e7;
  --ev-purple-300: #b98fd0;
  --ev-purple-400: #8c4cb0;
  --ev-purple-500: #6a2497;
  --ev-purple-600: #59197f;
  --ev-purple-700: #49136f;  /* PRIMARY — brand purple */
  --ev-purple-800: #380f57;
  --ev-purple-900: #2a0e3d;  /* deep plum — dark sections */

  /* ---- Accent: amber/coral (CTA highlights, gradient terminus) ---- */
  --ev-amber-400: #f59440;
  --ev-amber-500: #e8771f;   /* links/labels on dark, gradients, emphasis (NOT for white text: 2.96:1) */
  --ev-amber-600: #b8500f;   /* darkened from #c85a13 → 5.0:1 with white (accent-button hover, WCAG AA) */
  --ev-magenta-500: #c0397a; /* gradient midpoint */

  /* ---- Official brand orange (logo mascot) — stored for future use ---- */
  --ev-orange-light: #ff8b00; /* light orange — logo gradient start */
  --ev-orange-dark:  #e45402; /* darker orange — logo gradient end */
  --ev-amber-50:   #fbeede; /* warm tint behind amber accents (was a pasted hex) */
  --ev-danger:     #c0392b; /* "not recommended" / risk marks (was a pasted hex) */

  /* ---- Neutrals: slate ---- */
  --ev-white:     #ffffff;
  --ev-slate-50:  #f8fafc;
  --ev-slate-100: #f1f5f9;
  --ev-slate-200: #e2e8f0;
  --ev-slate-300: #cbd5e1;
  --ev-slate-400: #94a3b8;
  --ev-slate-500: #64748b;
  --ev-slate-700: #334155;
  --ev-slate-900: #0f172a;

  /* ---- Semantic status ---- */
  --ev-success: #15803d;   /* "open today", covered by insurance */
  --ev-success-soft: #f0fdf4;
  --ev-destructive: #ef4444;
  --ev-warning: #b45309;

  /* ============================================================
     SEMANTIC ALIASES  — use these in components, not raw scale values.
     Names mirror the product's shadcn token contract.
     ============================================================ */
  --background: var(--ev-white);
  --foreground: var(--ev-slate-900);

  --surface-page: #f7f3fb;   /* lavender app background — ~15% lighter than purple-50 */
  --surface-card: var(--ev-white);
  --surface-muted: var(--ev-slate-100);   /* section bands, inputs at rest */
  --surface-dark: var(--ev-purple-900);   /* plum feature bands */
  --surface-photo: #fcf7ff;  /* client-specified ground behind cut-out patient photos */

  --card: var(--ev-white);
  --card-foreground: var(--ev-slate-900);

  --primary: var(--ev-purple-700);
  --primary-hover: var(--ev-purple-800);
  --primary-foreground: #faf7fd;

  --secondary: var(--ev-slate-100);
  --secondary-foreground: var(--ev-slate-900);

  /* Deep accessible amber: 4.65:1 against the white button label (WCAG AA).
     The brighter --ev-amber-500 stays for gradients and text-on-dark, but must
     never sit behind white text. */
  --accent: #c05413;
  --accent-foreground: var(--ev-white);

  --muted: var(--ev-slate-100);
  --muted-foreground: #5e6e85; /* AA-compliant on white & purple-50 surfaces (was slate-500) */

  --text-body: var(--ev-slate-700);
  --text-heading: var(--ev-slate-900);
  --text-subtle: #5e6e85;
  --text-on-dark: #f3ecf8;
  --text-on-dark-subtle: #c7b3d6;

  --border: var(--ev-purple-100);       /* hairlines are brand lavender everywhere (2026-08 sweep) */
  --border-strong: var(--ev-purple-200);
  --input: var(--ev-slate-200);
  --ring: var(--ev-purple-700);

  --destructive: var(--ev-destructive);
  --destructive-foreground: #fef2f2;

  /* ---- Brand display gradient — ALWAYS purple → orange ----
     Two and only two variants:
       --gradient-brand        on LIGHT backgrounds  (.ev-gradient-text)
       --gradient-brand-light  on DARK backgrounds   (.ev-gradient-text-light)
     Both run purple → magenta → amber, left→right. Do not hand-roll new
     gradient-text values in components; use the two helper classes. */
  /*
   * TEXT-RAMP CONTRAST (WCAG AA large text, 3:1 minimum — measured 2026-08-28,
   * ADA review). Each ramp's weakest END STOP is pinned to a literal chosen as
   * the SMALLEST shift that clears the bar; every other use of amber/purple
   * keeps its token. Worst-case ratios at these stops:
   *   --gradient-brand        #d96a10 end: 3.49 on white, 3.18 on --surface-page
   *   --gradient-brand-light  #8a48c4 start: 3.10 on --ev-purple-900
   * These are deliberate minimums (the practice asked for the change to stay
   * minor and may tune the hue) — do not darken #d96a10's background pairing
   * or lighten plum without re-running the math. And -light is certified for
   * purple-900-family surfaces ONLY; on purple-700 its start measures 2.37
   * and fails, so do not use it there.
   */
  --gradient-brand: linear-gradient(95deg, var(--ev-purple-500) 0%, var(--ev-magenta-500) 52%, #d96a10 100%); /* @kind color */
  --gradient-brand-light: linear-gradient(95deg, #8a48c4 0%, var(--ev-magenta-500) 52%, var(--ev-amber-500) 100%); /* @kind color */ /* same magenta+amber as --gradient-brand; purple lifted further for dark-bg AA (see note above) */
  --gradient-soft: linear-gradient(180deg, var(--ev-purple-50) 0%, var(--ev-white) 100%);
  --gradient-hero: linear-gradient(168deg, #f7f2fb 0%, #f3ecf8 46%, var(--ev-white) 100%); /* the light page-hero wash (was pasted into 17 files) */

  /* ---- Global navigation bar — deep-plum gradient (both desktop & mobile) ---- */
  --gradient-nav: linear-gradient(90deg, #2a0e3d 0%, #3c1160 54%, #4a1570 100%); /* @kind color */
}

/* ====== tokens/typography.css ============================================ */
/* ============================================================
   Empire Vein & Vascular — TYPOGRAPHY TOKENS
   Inter throughout. Display = Inter 700–800, tight tracking.
   Eyebrow labels = monospace, uppercase, wide tracking (clinical motif).
   ============================================================ */

:root {
  --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-mono: var(--font-sans);   /* mono retired — labels now use the core font (kept as alias so usages don't break) */

  /* ---- Type scale (1.250 major-third-ish, clamped for display) ---- */
  --text-xs:   0.75rem;   /* 12 — fine print, legal */
  --text-sm:   0.875rem;  /* 14 — secondary, captions */
  --text-base: 1rem;      /* 16 — body */
  --text-lg:   1.125rem;  /* 18 — lead paragraph (17 on phones, see the query below) */
  --text-xl:   1.25rem;   /* 20 */
  --text-2xl:  1.5rem;    /* 24 — card titles */
  --text-3xl:  1.875rem;  /* 30 — section heads */
  --text-4xl:  2.25rem;   /* 36 */
  --text-5xl:  3rem;      /* 48 — hero */
  --text-6xl:  3.75rem;   /* 60 — display */

  /* ---- Weights ---- */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-extrabold: 800;

  /* ---- Line heights ---- */
  --leading-none: 1;
  --leading-tight: 1.1;
  --leading-snug: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.65;

  /* ---- Letter spacing ---- */
  --tracking-tighter: -0.03em;  /* display headlines */
  --tracking-tight: -0.015em;   /* headings */
  --tracking-normal: 0;
  --tracking-wide: 0.05em;
  --tracking-widest: 0.18em;    /* mono eyebrow labels */

  /* ---- Semantic roles ---- */
  --display-size: clamp(2.5rem, 5vw, 3.75rem);
  --display-weight: var(--weight-extrabold);
  --display-tracking: var(--tracking-tighter);
  --display-leading: var(--leading-tight);

  --eyebrow-size: var(--text-sm);
  --eyebrow-tracking: var(--tracking-widest);
}

/* ── Phone: take the paragraph size down one notch ───────────────────────
   --text-lg is the only step in the copy range with room to give. --text-base
   is already 16px and stays there: below 16 costs legibility for the older
   patients this site is read by, and iOS zooms the viewport on any input
   under 16px. So the nudge lands on the size that actually reads too large on
   a phone — .ev-lead, .evw-article-body, and the ~10 paragraphs that set
   font-size:var(--text-lg) inline.

   17px keeps a visible step above 16px body while pulling the lead back under
   the hero h1 (presets.css raises that end at the same 640px boundary).
   Tablet and desktop keep 18px — this query never reaches them. */
@media (max-width: 640px) {
  :root { --text-lg: 1.0625rem; }  /* 18px → 17px */
}

/* ====== tokens/spacing.css =============================================== */
/* ============================================================
   Empire Vein & Vascular — SPACING, RADIUS & LAYOUT TOKENS
   4px base grid. Radius base 0.5rem (shadcn --radius), with the
   xl card radius used on the product's primary cards.
   ============================================================ */

:root {
  /* ---- Spacing scale (4px grid) ---- */
  --space-0: 0;
  --space-1: 0.25rem;  /* 4 */
  --space-2: 0.5rem;   /* 8 */
  --space-3: 0.75rem;  /* 12 */
  --space-4: 1rem;     /* 16 */
  --space-5: 1.25rem;  /* 20 */
  --space-6: 1.5rem;   /* 24 — card padding */
  --space-8: 2rem;     /* 32 */
  --space-10: 2.5rem;  /* 40 — feature card padding */
  --space-12: 3rem;    /* 48 */
  --space-16: 4rem;    /* 64 — section rhythm */
  --space-20: 5rem;    /* 80 */
  --space-24: 6rem;    /* 96 — large section rhythm */

  /* ---- Radius (shadcn --radius = 0.5rem) ---- */
  --radius-sm: 0.25rem;  /* calc(base - 4px) */
  --radius-md: 0.375rem; /* calc(base - 2px) */
  --radius: 0.5rem;      /* base — buttons, inputs */
  --radius-lg: 0.5rem;
  --radius-xl: 0.75rem;  /* cards */
  --radius-2xl: 1rem;
  --radius-full: 9999px; /* pills, avatars, icon buttons */

  /* ---- Layout ---- */
  --container-max: 1660px;
  --container-pad: 24px;
  --section-y: clamp(3.5rem, 6vw, 5rem); /* @kind spacing */
  --section-pad: clamp(56px, 7vw, 104px); /* @kind spacing */ /* vertical padding of a full page section (the value the pages actually use) */
  /* The floor is what phones get: 4vw only overtakes 16px above ~400px and
     the 40px ceiling only bites above 1000px, so lowering the floor from
     20px to 16px narrows the page margin on phones by 20% and leaves every
     larger width untouched. */
  --gutter: clamp(16px, 4vw, 40px);       /* @kind spacing */ /* horizontal page gutter — every container edge */
  --section-pad-x: clamp(20px, 4vw, 40px); /* @kind spacing */ /* .ev-section side padding; mobile swipe rails cancel exactly this */
}

/* ====== tokens/effects.css =============================================== */
/* ============================================================
   Empire Vein & Vascular — EFFECTS: shadows, focus, transitions
   Soft, low-contrast elevation (clinical, trustworthy). The product
   uses a Replit "elevate" overlay on hover/press — emulated here as
   --elevate-hover / --elevate-press tints.
   ============================================================ */

:root {
  /* ---- Shadows ---- */
  --shadow-xs: 0 1px 2px 0 rgb(15 23 42 / 0.05);
  --shadow-sm: 0 1px 3px 0 rgb(15 23 42 / 0.08), 0 1px 2px -1px rgb(15 23 42 / 0.06);
  --shadow:    0 2px 6px -1px rgb(15 23 42 / 0.09), 0 1px 3px -1px rgb(15 23 42 / 0.06);
  --shadow-md: 0 6px 16px -4px rgb(42 14 61 / 0.13), 0 2px 6px -2px rgb(15 23 42 / 0.07);
  --shadow-lg: 0 12px 28px -8px rgb(42 14 61 / 0.18), 0 4px 10px -4px rgb(15 23 42 / 0.08);
  --shadow-plum: 0 18px 40px -12px rgb(42 14 61 / 0.45);

  /* ---- Interaction overlays (Replit "elevate" model) ---- */
  --elevate-hover: rgb(15 23 42 / 0.04);   /* subtle darken on hover */
  --elevate-press: rgb(15 23 42 / 0.08);   /* deeper on active */

  /* ---- Focus ring ----
     One keyboard focus style for the whole site (base.css applies it via
     :focus-visible): an amber outline that reads on white, lavender and plum. */
  --focus-outline: 3px solid var(--ev-amber-500);
  --focus-outline-offset: 2px;

  /* ---- Motion ---- */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1); /* @kind other */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* @kind other */
  --duration-fast: 120ms; /* @kind other */
  --duration: 180ms; /* @kind other */
  --duration-slow: 280ms; /* @kind other */
  --transition-colors: color var(--duration) var(--ease-standard), background-color var(--duration) var(--ease-standard), border-color var(--duration) var(--ease-standard);
}

/* ====== tokens/base.css ================================================== */
/* ============================================================
   Empire Vein & Vascular — base element defaults.
   Lightweight resets + brand defaults so specimen cards and kits
   inherit the right body font, color, and rendering.
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

/* The [hidden] attribute must actually hide.
   The UA stylesheet's [hidden] { display: none } is a plain author-level-zero
   rule, so ANY inline display beats it — and the blocks set inline display on
   almost everything, because that is how the prototype's styles were ported.
   The result was elements marked hidden that were fully laid out and visible:
   the booking form showed its Back and Submit buttons on step one (which is
   also what pushed /book-online/ 72px wider than a 375px screen), and the vein
   quiz's insight slide would have shown all twelve notes and both alert panels
   at once instead of the two-to-four the answers select.
   Found 2026-08-25 by comparing computed display against the attribute; every
   check before it read the .hidden PROPERTY, which was correct the whole time.
   !important is deliberate: it has to beat an inline style. */
[hidden] { display: none !important; }

/* ---- Two blocks that were laid out for desktop widths only ----
   Both fixed 2026-08-25 after measuring every page at 375px. Each overrides an
   inline style the block writes, so !important is required, the same way
   .ev-booking-rail already does. Desktop rendering is unchanged. */

/* ── Vein disease progression: one card per stage ──────────────────────
   The photo and the numbered step were two separate grids, aligned only by
   coincidence of having the same column count. Any reflow broke the pairing,
   and on a phone the reader got every photo first and every step after.

   display:contents dissolves the card into the parent grid on desktop, so its
   photo lands on row 1 and its text on row 2 of its own column — pixel-wise
   the same two aligned rows as before. Below 640px (see the query above) the
   card becomes a real flex box and the two halves travel together.

   These sit BEFORE the max-width:640px query below on purpose. Same
   specificity, so source order decides: appended after it, the unconditional
   display:contents beat the query display:flex and the phone layout silently
   stayed at four columns. */
.ev-stage-card { display: contents; }
.ev-stage-card > * { grid-column: var(--col); }
.ev-stage-photo { grid-row: 1; }
.ev-stage-text  { grid-row: 2; position: relative; z-index: 1; }
/* Spans every column on the text row, sitting up in the gap above the badges
   where the old absolutely-positioned copy sat. */
/* display lives here, not inline: an inline display:block beats the
   max-width:640px rule that hides this line, so the flow line kept drawing
   across a two-column phone layout it was never drawn for. */
.ev-stage-flow {
  display: block;
  grid-column: 1 / -1; grid-row: 2;
  align-self: start; margin-top: -19px; z-index: 0;
}


/* vein-progression: one column per stage reads well until it does not. Four
   columns on a phone leaves 68px each — one word per line — and the fixed 74px
   pulse ring hangs past the last column and scrolls the page. The flow line
   underneath is drawn for a single row, so it steps aside too. */
@media (max-width: 640px) {
  /* Two stages a row, so a four-stage progression reads 1,2 / 3,4. Each cell
     is now a whole card — photo AND text — so the pairing survives. */
  .ev-stage-pairs { grid-template-columns: repeat(2, 1fr) !important; row-gap: 30px; }
  .ev-stage-card { display: flex; flex-direction: column; gap: 14px; }
  .ev-stage-flow { display: none; }
}

/* Buttons must be allowed to wrap when the label is long.
   Every .ev-btn carries inline height:48px and white-space:nowrap, ported from
   the prototype where the labels are English. Spanish runs 20-30% longer and
   sometimes far more: "Book a free vein screening" becomes "Reserva una
   revisión gratuita de venas.", which at 375px is a single unbreakable line
   wider than the phone — /es/vein-services/venaseal/ scrolled sideways 32px
   while the English page was fine.

   Below 640px the label wraps and the button grows to fit instead. min-height
   keeps the touch target; !important is needed because the height and nowrap
   are inline on 23 blocks. Found 2026-08-25 by sweeping the Spanish pages at
   phone width — no English-only pass would have caught it. */
@media (max-width: 640px) {
  .ev-btn {
    white-space: normal !important;
    height: auto !important;
    min-height: 48px;
    padding-block: 12px !important;
    text-align: center;
    line-height: 1.25 !important;
  }
}

/* insurance-bar: the "98% of procedures are covered" pair is flex:0 0 auto, so
   it never shrinks. Below 720px it takes its own row and loses the divider that
   only makes sense beside something. */
@media (max-width: 719px) {
  .ev-insbar-stat {
    flex: 1 1 100% !important;
    min-width: 0;
    padding-right: 0 !important;
    border-right: 0 !important;
  }
}

body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--text-body);
  background: var(--background);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  line-height: var(--leading-relaxed);
}

/* Native form controls do NOT inherit the page font by default — without
   this, every button/input that doesn't set fontFamily renders in the OS
   font (Arial/Segoe) instead of Inter. */
button, input, select, textarea { font-family: inherit; }

h1, h2, h3, h4, h5, h6 {
  color: var(--text-heading);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-snug);
  margin: 0;
  text-wrap: balance;
}

p { margin: 0; }

a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ============================================================
   Accessibility (WCAG 2.1 AA)
   ------------------------------------------------------------
   • Visible keyboard focus for every interactive element
     (native + elements upgraded by the a11y enhancer).
   • Skip-to-content link.
   • Screen-reader-only utility.
   ============================================================ */
:focus-visible {
  outline: var(--focus-outline);
  outline-offset: var(--focus-outline-offset);
  border-radius: 3px;
}
/* Don't show the ring on mouse click, only keyboard (browsers handle :focus-visible) */
:focus:not(:focus-visible) { outline: none; }

.ev-skip-link {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 1000;
  transform: translateY(-160%);
  background: var(--ev-purple-700, #5a2a86);
  color: #fff;
  padding: 12px 20px;
  border-radius: var(--radius-md, 8px);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
  box-shadow: 0 8px 24px rgba(0,0,0,0.25);
  transition: transform 0.18s ease;
}
.ev-skip-link:focus {
  transform: translateY(0);
  text-decoration: none;
  outline: 3px solid var(--ev-amber-500, #f59440);
  outline-offset: 2px;
}

.ev-sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@keyframes evFade {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

.ev-noscroll::-webkit-scrollbar { display: none; }
.ev-noscroll { -ms-overflow-style: none; scrollbar-width: none; }
@keyframes evDrawerIn { from { transform: translateX(100%); } to { transform: translateX(0); } }

/* Video testimonial cards: gentle lift + play-button grow on hover/focus */
.ev-play-btn { transition: transform 0.25s cubic-bezier(.22,.61,.36,1), background 0.25s ease; }
button:hover .ev-play-btn, button:focus-visible .ev-play-btn { transform: translate(-50%, -50%) scale(1.12); background: rgba(255,255,255,0.4); }

/* Vertical social-style video overlay (VideoStory): live dot + caption swap */
@keyframes evPulseDot { 0% { box-shadow: 0 0 0 0 rgba(255,90,90,0.6); } 70% { box-shadow: 0 0 0 7px rgba(255,90,90,0); } 100% { box-shadow: 0 0 0 0 rgba(255,90,90,0); } }
@keyframes evCaptionIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* Testimonial grid → swipe carousel on mobile. Threshold matches the site's
   phone/tablet split (tablet breakpoint is 900px for the 2-col grid; this
   carousel range is wider than the old 600px cutoff so it reliably covers
   the full phone width range, not just small phones). */
.ev-testi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
@media (max-width: 900px) { .ev-testi-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767px) {
  .ev-testi-grid { display: flex; grid-template-columns: none; gap: 14px; overflow-x: auto; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; padding: 4px 0 12px; margin: 0 -24px; padding-left: 24px; padding-right: 24px; scrollbar-width: none; }
  .ev-testi-grid::-webkit-scrollbar { display: none; }
  .ev-testi-grid > * { flex: 0 0 76%; scroll-snap-align: center; }
}

/* Global divider grid — column separation in place of card boxes.
   Faint vertical rules between columns (per-cell border-left, set inline).
   Collapses to stacked rows with top rules on mobile. */
@media (max-width: 760px) {
  .ev-divider-grid { grid-template-columns: 1fr !important; }
  .ev-divider-grid > * { border-left: none !important; padding-left: 0 !important; padding-right: 0 !important; border-top: 1px solid var(--border) !important; padding-top: 26px !important; margin-top: 4px; }
  .ev-divider-grid > *:first-child { border-top: none !important; padding-top: 0 !important; margin-top: 0; }
}

/* ---- Global section rhythm — ONE source of truth ----
   The standard content-section spacing used across every subpage. Replaces the
   inline `{ ...wrap, padding: "clamp(64px, 8.5vw, 124px) clamp(20px, 4vw, 40px)" }`
   that was repeated ~100×. Gives a constrained, centered container plus the
   generous vertical rhythm that lets each section be absorbed on its own, with
   consistent breathing room between sections page to page. Adjust the numbers
   HERE and the whole site updates. Heroes and colored feature bands intentionally
   set their own (tighter/asymmetric) padding inline and are left untouched. */
.ev-section {
  max-width: var(--container-max);
  margin-inline: auto;
  /* --section-pad-x is a token because the mobile swipe rails have to cancel
     exactly this much to reach the screen edge. It was written out twice —
     here and as a magic 20px default in .ev-swipe-row — and the two agreeing
     was luck: --gutter's floor was moved 20px -> 16px in the mobile pass and
     nothing would have caught this one drifting the same way. A bleed wider
     than the padding scrolls the whole PAGE sideways. */
  padding: clamp(64px, 8.5vw, 124px) var(--section-pad-x);
}

/* ---- Touch target helper (WCAG 2.5.5 / mobile) ----
   Many patients are 45+; primary controls must be comfortably tappable. Apply
   .ev-tap to any interactive control that would otherwise render under ~44px on
   a phone. It reserves a 44px hit height without forcing the visual box larger
   (the flex centering keeps existing text/padding looking the same). Add
   .ev-tap--icon for icon-only buttons that also need a 44px width. */
.ev-tap { min-height: 44px; display: inline-flex; align-items: center; }
.ev-tap--icon { min-height: 44px; min-width: 44px; justify-content: center; }

/* ---- Hero stat row (SubpageHero `stats`) mobile stacking ----
   .ev-herostats builds N inline columns with per-cell hairline rules. It has no
   reflow by design, so a 3-stat row crushes to ~118px cells on a phone. On small
   screens, stack it to a single readable column and swap the left rules for top
   rules (mirrors the ev-divider-grid mobile treatment). */
@media (max-width: 560px) {
  .ev-herostats { grid-template-columns: 1fr !important; }
  .ev-herostats > * { border-left: none !important; padding-left: 0 !important; border-top: 1px solid var(--border); padding-top: 16px; }
  .ev-herostats > *:first-child { border-top: none; padding-top: 0; }
}

/* Home hero: on phones, blow "ADVANCED" up so it fills ~90% of the width and
   reads as the dominant element, and push the "Minimally-invasive…" subline a
   little lower so it stops crowding the big word. Bespoke home display type
   (exempt from the type scale). vw coefficient tuned so the 8-char word spans
   ~90% across the 360–430px phone range without triggering horizontal scroll. */
@media (max-width: 640px) {
  .ev-hero-advanced { font-size: 17.5vw !important; }
  .ev-hero-sub { margin-top: 10px !important; font-size: 7.5vw !important; line-height: 1.18 !important; }
}

/* FAQ category tabs are centered on desktop, but center + overflow-x hides the
   first tab behind the left edge (unreachable) once they overflow on a phone.
   Left-align on mobile so the scroll starts at the first tab. */
@media (max-width: 760px) { .ev-faq-tabs { justify-content: flex-start !important; } }

/* Home locations teaser office tiles → single column on small phones so each
   office's name/address/phone has room to read (was a fixed 2-col grid). */
@media (max-width: 560px) { .ev-home-offices { grid-template-columns: 1fr !important; } }

/* ---- Stacked stat rows: drop orphaned vertical hairlines on mobile ----
   QuickFacts and StatBand set per-cell border-left inline for their desktop
   columns. When they wrap/collapse to one column on phones, those become stray
   left borders. .ev-statrow strips them (and the paired left padding) on mobile. */
@media (max-width: 560px) {
  .ev-statrow > * { border-left: none !important; padding-left: 0 !important; }
}

/* ---- Process / "How it works" numbered steps ----
   Even columns so a step never orphans onto a new row (the auto-fit grid this
   replaces left a lone step stranded on desktop when the count didn't divide
   evenly). Set --steps-cols inline to the step count for the desktop layout;
   collapses to 2-up on tablet, then 1-up on mobile. */
.ev-steps {
  display: grid;
  gap: clamp(28px, 3vw, 40px);
  grid-template-columns: repeat(var(--steps-cols, 4), minmax(0, 1fr));
}
@media (max-width: 900px) { .ev-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .ev-steps { grid-template-columns: 1fr; } }

/* Booking page — two-column layout (persuasive rail + form card) */
.ev-booking-layout { display: grid; grid-template-columns: 1fr; gap: 28px; }
/* Grid items default to min-width:auto, so a non-wrapping child (the mobile
   insurance swipe row) would stretch its track past the viewport instead of
   scrolling. Zero it so wide content scrolls inside its own row. */
.ev-booking-layout > * { min-width: 0; }
@media (min-width: 940px) {
  .ev-booking-layout {
    grid-template-columns: 0.7fr 1fr;
    gap: 56px;
    align-items: start;
    grid-template-areas: "head form" "trust form";
    grid-template-rows: auto 1fr;
  }
  .ev-booking-head  { grid-area: head; }
  .ev-booking-trust { grid-area: trust; }
  .ev-booking-form  { grid-area: form; }
}

/* ---- Mobile horizontal carousel utility ----
   A swipeable single row for compact content on phones (insurance logo pills,
   stat chips). Children keep their natural width; scrollbar hidden; edge-bleed
   so the row swipes from screen edge to screen edge. */
/* ── Horizontal scroll affordance ─────────────────────────────────────────
   A wide table inside overflow-x:auto scrolls perfectly well, but on a phone
   nothing SAYS so: iOS and Android render no persistent scrollbar, and the
   two comparison tables on /knee-pain/ hide 243px and 280px of themselves —
   about 45% of each — with no cue at all. Desktop gets a scrollbar and so
   the gap is invisible unless you measure a phone.

   This is the background-attachment shadow. The two `local` layers scroll
   WITH the content and mask the shadow at each end; the two `scroll` layers
   stay put and draw it. So a soft shadow shows only on the side that still
   has content left, and fades out when you reach that edge — the affordance
   is self-updating with no JavaScript and no extra markup, and it degrades
   to a plain scroller wherever background-attachment is unsupported.

   Use this instead of hand-writing overflow-x:auto on a wide block. Not to
   be confused with .ev-hscroll, which is a snapping flex RAIL that
   deliberately hides its scrollbar. */
.ev-scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(to right, var(--background) 40%, rgba(255, 255, 255, 0)) left center,
    linear-gradient(to left, var(--background) 40%, rgba(255, 255, 255, 0)) right center,
    radial-gradient(farthest-side at 0 50%, rgba(42, 14, 61, 0.17), rgba(42, 14, 61, 0)) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(42, 14, 61, 0.17), rgba(42, 14, 61, 0)) right center;
  background-repeat: no-repeat;
  background-size: 44px 100%, 44px 100%, 16px 100%, 16px 100%;
  background-attachment: local, local, scroll, scroll;
}

.ev-hscroll { display: flex; gap: 12px; overflow-x: auto; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; -ms-overflow-style: none; scrollbar-width: none; margin-inline: -20px; padding: 2px 20px 6px; }
.ev-hscroll::-webkit-scrollbar { display: none; }
.ev-hscroll > * { flex: 0 0 auto; scroll-snap-align: start; }

/* Rail arrows, disabled by 15-rail.js at the ends of their track. Dimming is
   the point: the pair has to visibly respond to the scroll or it reads as
   decoration, which is how a dead pair of arrows got shipped unnoticed. */
[data-ev-rail-dir][disabled] { opacity: 0.4; cursor: default; }

/* Booking page mobile: surface "insurance we accept" HIGH on the page as a
   compact swipe row (hidden on desktop, which keeps the full bottom strip),
   and turn the stacked trust stats into a swipe row so the form stays close
   to the top. */
.ev-booking-inslogos { display: none; }
@media (max-width: 939px) {
  .ev-booking-inslogos { display: block; }
  /* Phones: the lead paragraph steps aside so the form follows the heading and the insurance row (client, 2026-09-10). */
  .ev-booking-lead { display: none; }
  .ev-booking-insfoot { display: none; }
  .ev-booking-rail { flex-direction: row !important; gap: 0 !important; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: none; scrollbar-width: none; margin-inline: -20px; padding-inline: 20px; }
  .ev-booking-rail::-webkit-scrollbar { display: none; }
  .ev-booking-rail > * { flex: 0 0 auto; min-width: 44%; padding-right: 24px; }
}

/* Booking form — inline field labels on desktop, stacked on mobile */
.ev-field-inline { display: grid; grid-template-columns: 1fr; gap: 6px; }
@media (min-width: 640px) {
  .ev-field-inline { grid-template-columns: 150px minmax(0, 1fr); gap: 20px; align-items: start; }
  .ev-field-inline > label { padding-top: 13px; }
}

/* Locations page — office cards: 3 across on desktop, 2 then 1 as it narrows */
.ev-loc-cards { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1024px) {
  .ev-loc-cards { grid-template-columns: repeat(2, 1fr) !important; }
}
@media (max-width: 620px) {
  .ev-loc-cards { grid-template-columns: 1fr !important; }
}

/* Locations page — card head: office name and the open/reopens status share a
   row until the name needs two lines for itself. Below 767px the status, which
   never shrinks, was being pushed past the card edge and clipped, so the head
   stacks instead. The flex lives here rather than inline on the div for the
   same reason: overriding an inline align-items would have taken !important. */
.ev-loc-cardhead { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
@media (max-width: 767px) {
  .ev-loc-cardhead { flex-direction: column; align-items: flex-start; gap: 6px; }
}

/* Brand display gradient text helpers — purple → orange, always.
   .ev-gradient-text        → on light backgrounds
   .ev-gradient-text-light  → the lighter variant, on dark backgrounds */
.ev-gradient-text,
.ev-gradient-text-light {
  -webkit-background-clip: text;
  background-clip: text;
  /* Only the FILL is transparent. `color` is left inherited on purpose, so
     currentColor inside these spans is the surrounding text color — the
     reveal below wipes from exactly that color to the gradient, in any
     context, with no per-context configuration. */
  -webkit-text-fill-color: transparent;
  /* background-clip:text only paints the gradient inside the box; with the tight
     display line-heights used here, ascenders/descenders (y, p, g) fall outside
     and render invisible. Extend the paint area a touch, then pull it back with a
     matching negative margin so layout is unchanged. */
  padding-block: 0.14em;
  margin-block: -0.14em;
}
.ev-gradient-text { --gx-ramp: var(--gradient-brand); background-image: var(--gx-ramp); }
.ev-gradient-text-light { --gx-ramp: var(--gradient-brand-light); background-image: var(--gx-ramp); }

/* ---- Animated morphing gradient field (glassmorphism emphasis section) ---- */
.ev-morph {
  position: absolute;
  inset: -20%;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.ev-blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  opacity: 0;
  transition: opacity 1.2s ease;
  will-change: transform;
}
.ev-morph.is-active .ev-blob { opacity: var(--blob-opacity, 0.5); }
.ev-blob-a {
  width: 46%; height: 60%; top: -6%; left: -4%;
  background: radial-gradient(circle at 50% 50%, #b07fd0, transparent 70%);
  --blob-opacity: 0.5; /* @kind other */
}
.ev-blob-b {
  width: 42%; height: 56%; top: 18%; right: -6%;
  background: radial-gradient(circle at 50% 50%, #c0397a, transparent 70%);
  --blob-opacity: 0.42; /* @kind other */
}
.ev-blob-c {
  width: 40%; height: 52%; bottom: -10%; left: 30%;
  background: radial-gradient(circle at 50% 50%, #f59440, transparent 70%);
  --blob-opacity: 0.34; /* @kind other */
}
.ev-morph.is-active .ev-blob-a { animation: ev-blob-a 22s ease-in-out infinite; }
.ev-morph.is-active .ev-blob-b { animation: ev-blob-b 26s ease-in-out infinite; }
.ev-morph.is-active .ev-blob-c { animation: ev-blob-c 30s ease-in-out infinite; }
@keyframes ev-blob-a {
  0%, 100% { transform: translate(-8%, -6%) scale(1); }
  50%      { transform: translate(14%, 12%) scale(1.18); }
}
@keyframes ev-blob-b {
  0%, 100% { transform: translate(8%, 6%) scale(1.08); }
  50%      { transform: translate(-14%, -10%) scale(0.9); }
}
@keyframes ev-blob-c {
  0%, 100% { transform: translate(0%, 8%) scale(1); }
  50%      { transform: translate(-10%, -8%) scale(1.2); }
}
/* Frosted glass card sitting over the morph field */
.ev-glass-card {
  position: relative;
  z-index: 1;
  background: rgba(255, 255, 255, 0.55);
  backdrop-filter: blur(16px) saturate(1.2);
  -webkit-backdrop-filter: blur(16px) saturate(1.2);
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 24px 60px -28px rgba(73, 19, 111, 0.28);
}
@media (prefers-reduced-motion: reduce) {
  .ev-morph.is-active .ev-blob { animation: none; }
}

/* ---- Vertical symptom-image mosaic (stationary text / scrolling images) ---- */
.ev-mosaic {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  height: 100%;
  max-height: 620px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 11%, #000 89%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 11%, #000 89%, transparent 100%);
}
.ev-mosaic-col { display: flex; flex-direction: column; gap: 18px; will-change: transform; }
.ev-mosaic-col.up   { animation: ev-mosaic-up   42s linear infinite; }
.ev-mosaic-col.down { animation: ev-mosaic-down 50s linear infinite; }
.ev-mosaic:hover .ev-mosaic-col { animation-play-state: paused; }
.ev-mosaic-tile {
  border-radius: var(--radius-xl);
  overflow: hidden;
  flex-shrink: 0;
  box-shadow: 0 14px 36px -16px rgba(31,18,46,0.42);
  background: var(--ev-purple-100);
}
.ev-mosaic-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
@keyframes ev-mosaic-up   { from { transform: translateY(0); } to { transform: translateY(-50%); } }
@keyframes ev-mosaic-down { from { transform: translateY(-50%); } to { transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .ev-mosaic-col.up, .ev-mosaic-col.down { animation: none; }
}

/* ---- Count-aware card grids (no orphan rows) ----
   auto-fit grids strand odd items on a lone row at in-between widths
   (4 cards → 3 + 1). These classes pick only column counts that divide
   the item count evenly: a 4-card grid is 1 → 2×2 → 4-across, never 3.
   Apply the class matching the number of children (see S.TintCards). */
.ev-grid-cards { display: grid; gap: 20px; grid-template-columns: 1fr; }
@media (min-width: 700px) { .ev-grid-cards-2, .ev-grid-cards-4, .ev-grid-cards-6 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 900px) { .ev-grid-cards-3, .ev-grid-cards-6 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1240px) { .ev-grid-cards-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* ---- Vein quiz photo-match grid: one row of 4 on desktop, 2x2 on mobile ---- */
.ev-quiz-photos { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 719px) {
  .ev-quiz-photos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 860px) {
  .ev-symptom-grid { grid-template-columns: 1fr !important; }
  .ev-mosaic { max-height: 420px; }
}

/* ---- In the News: Wired-style hero + editorial category nav ---- */
.ev-blog-hero { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: clamp(26px, 3vw, 48px); align-items: start; }
.ev-blog-cats { display: flex; justify-content: center; flex-wrap: wrap; gap: clamp(16px, 2.6vw, 34px); border-bottom: 1px solid var(--border); }
.ev-blog-cats::-webkit-scrollbar { display: none; }
@media (max-width: 860px) {
  .ev-blog-hero { grid-template-columns: 1fr; gap: 30px; }
}

/* ---- Flowing wavy lines in the white space beside the treatments heading ---- */
.ev-waves { position: absolute; top: 48px; right: 0; width: clamp(520px, 64vw, 1040px); height: 240px; z-index: 0; pointer-events: none; -webkit-mask-image: linear-gradient(to right, transparent 0, #000 34%); mask-image: linear-gradient(to right, transparent 0, #000 34%); }
.ev-wave { fill: none; stroke: url(#evWaveGrad); stroke-width: 3; stroke-linecap: round; stroke-dasharray: 11 15; animation-name: ev-wave-flow; animation-timing-function: linear; animation-iteration-count: infinite; }
@keyframes ev-wave-flow { to { stroke-dashoffset: -520; } }
@media (prefers-reduced-motion: reduce) { .ev-wave { animation: none; } }
@media (max-width: 1024px) { .ev-waves { display: none; } }

/* Gentle slide-in for each symptom-quiz question */
@keyframes ev-quiz-in {
  from { opacity: 0; transform: translateX(28px); }
  to   { opacity: 1; transform: translateX(0); }
}
.ev-quiz-step { animation: ev-quiz-in 420ms cubic-bezier(0.32, 0.72, 0, 1); }
@media (prefers-reduced-motion: reduce) {
  .ev-quiz-step { animation: none; }
}

/* ---- GAE scroll-reveal: visible by default; hidden only before reveal ---- */
.ev-gae-step { opacity: 1; transform: translateY(0); transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.32, 0.72, 0, 1); }
.ev-gae:not(.is-in) .ev-gae-step { opacity: 0; transform: translateY(18px); }
.ev-gae-letter { transition: filter 0.7s ease, opacity 0.7s ease; opacity: 1; }
.ev-gae.is-in .ev-gae-step:nth-of-type(1) { transition-delay: 0.05s; }
.ev-gae.is-in .ev-gae-step:nth-of-type(2) { transition-delay: 0.45s; }
.ev-gae.is-in .ev-gae-step:nth-of-type(3) { transition-delay: 0.85s; }
.ev-gae:not(.is-in) .ev-gae-step:nth-of-type(1) .ev-gae-letter,
.ev-gae:not(.is-in) .ev-gae-step:nth-of-type(2) .ev-gae-letter { opacity: 0.55; }
.ev-gae.is-in .ev-gae-step:nth-of-type(1) .ev-gae-letter,
.ev-gae.is-in .ev-gae-step:nth-of-type(2) .ev-gae-letter { opacity: 0.78; }
.ev-gae.is-in .ev-gae-step:nth-of-type(3) .ev-gae-letter { opacity: 1; }
.ev-gae-connector { stroke-dasharray: 6 8; opacity: 1; transition: opacity 0.6s ease; }
.ev-gae:not(.is-in) .ev-gae-connector { opacity: 0; }
.ev-gae.is-in .ev-gae-connector { animation: ev-wave-flow 14s linear infinite; transition-delay: 0.4s; }
@media (prefers-reduced-motion: reduce) {
  .ev-gae:not(.is-in) .ev-gae-step { opacity: 1; transform: none; }
  .ev-gae-step { transition: none; }
  .ev-gae:not(.is-in) .ev-gae-connector { opacity: 1; }
  .ev-gae.is-in .ev-gae-connector { animation: none; }
}

/* Monospace clinical eyebrow */
.ev-eyebrow {
  font-family: var(--font-mono);
  font-size: var(--eyebrow-size);
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  color: var(--muted-foreground);
}

/* ---- Subtle pointer arrow on the GAE before/after angiograms ---- */
.ev-point { position: absolute; z-index: 2; pointer-events: none; will-change: transform; }
.ev-point svg { display: block; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.35)); }
.ev-point-line { stroke-dasharray: 5 6; animation: ev-point-dash 1.4s linear infinite; }

/* ---- Brand flow line: signature flowing dashed line in the Empire ----
   purple → pink → amber colorway. Reusable connective motif (vascular
   treatments backdrop, vein-disease progression line, etc). ---- */
.ev-flowline { fill: none; stroke-width: 3; stroke-linecap: round; stroke-dasharray: 11 15; animation-name: ev-wave-flow; animation-timing-function: linear; animation-iteration-count: infinite; }
@media (prefers-reduced-motion: reduce) { .ev-flowline { animation: none; } }
/* Soft pulsing glow halo behind each stage point */
.ev-stage-pulse { position: absolute; top: 50%; left: 50%; width: 74px; height: 74px; margin: -37px 0 0 -37px; border-radius: 50%; pointer-events: none; z-index: 0; animation: ev-stage-pulse 2.9s ease-in-out infinite; }
@keyframes ev-stage-pulse { 0%, 100% { transform: scale(0.62); opacity: 0.25; } 50% { transform: scale(1.18); opacity: 0.8; } }
@media (prefers-reduced-motion: reduce) { .ev-stage-pulse { animation: none; } }
.ev-point-bob { animation: ev-point-bob 2.6s ease-in-out infinite; }
@keyframes ev-point-dash { to { stroke-dashoffset: -22; } }
@keyframes ev-point-bob {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-5px, -3px); }
}
@media (prefers-reduced-motion: reduce) {
  .ev-point-line { animation: none; }
  .ev-point-bob { animation: none; }
}

/* ---- Philosophy accordion (About Us): sticky heading + expanding list ---- */
.ev-philo-grid { display: grid; grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr); gap: clamp(36px, 6vw, 96px); align-items: start; }
.ev-philo-head { position: sticky; top: calc(var(--header-h, 72px) + 40px); }
@media (max-width: 900px) {
  .ev-philo-grid { grid-template-columns: 1fr; gap: 8px; }
  .ev-philo-head { position: static; }
}
.ev-philo-item { border-top: 1px solid var(--border); }
.ev-philo-item:first-child { border-top: none; }
.ev-philo-btn { display: flex; gap: clamp(18px, 2.4vw, 28px); align-items: flex-start; width: 100%; text-align: left; background: none; border: none; cursor: pointer; padding: clamp(22px, 2.6vw, 30px) 0; font: inherit; color: inherit; }
.ev-philo-thumb { flex: 0 0 auto; overflow: hidden; border-radius: var(--radius-lg); transition: width 0.42s cubic-bezier(0.32, 0.72, 0, 1), height 0.42s cubic-bezier(0.32, 0.72, 0, 1); }
.ev-philo-panel { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows 0.42s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.32s ease, margin-top 0.42s ease; margin-top: 0; }
.ev-philo-panel > div { overflow: hidden; }
.ev-philo-item.is-open .ev-philo-panel { grid-template-rows: 1fr; opacity: 1; margin-top: 12px; }
@media (prefers-reduced-motion: reduce) {
  .ev-philo-thumb, .ev-philo-panel { transition: none; }
}

/* Fullerton ad landing page (/fullerton-vein-care) */
/* Hero photo background. A light gradient veils the top (keeps the top-aligned
   copy + form readable) and fades out by ~60% so the beach photo shows cleanly
   at the bottom. The subject (woman + legs) sits on the LEFT of the photo, so
   it is pinned to "left bottom" and never cropped off. */
.flp-hero-bg {
  background-color: #eef1f5;
  background-image:
    linear-gradient(180deg, rgba(255,255,255,0.42) 0%, rgba(255,255,255,0.1) 22%, rgba(255,255,255,0) 44%),
    url('https://healthyveins.empirevein.com/wp-content/uploads/fullerton_bg_img.jpg');
  background-size: cover, cover;
  background-position: center, left bottom;
  background-repeat: no-repeat, no-repeat;
}
/* Phones: shift the crop toward the subject and dim the top a touch more so the
   stacked content stays legible. Swap in a mobile-cropped image here when one
   is supplied (replace the url() on the line below). */
@media (max-width: 700px) {
  .flp-hero-bg {
    background-image:
      linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0.15) 32%, rgba(255,255,255,0) 58%),
      url('https://healthyveins.empirevein.com/wp-content/uploads/fullerton_bg_img.jpg');
    background-position: center, 22% bottom;
  }
}
/* stack the two-column hero (copy + form) and the why/doctor split when narrow */
@media (max-width: 880px) {
  .flp-hero-grid { grid-template-columns: 1fr !important; }
}
@media (max-width: 820px) {
  .flp-doc-grid, .flp-vs-grid { grid-template-columns: 1fr !important; }
}
@media (max-width: 640px) {
  .flp-hide-sm { display: none !important; }
}
@media (max-width: 380px) {
  .flp-hide-xs { display: none !important; }
}
@media (max-width: 560px) {
  .flp-field-row { grid-template-columns: 1fr !important; }
}


/* Good-news reassurance block (editorial split): heading stacks above the
   message on phones. */
@media (max-width: 760px) {
  .ev-goodnews { grid-template-columns: 1fr !important; }
}

/* ---- Scroll reveal, site-wide (2026-08) -----------------------------
   Content SHIPS VISIBLE. The hiding state (.rv-armed) is applied by JS
   only to elements that start below the fold, so no-JS, print, crawlers,
   reduced-motion users, and anything above the fold (the LCP element in
   particular) are never hidden, delayed, or shifted.

   Three flavors:
     .ev-rv        one block fades + rises as a unit (section headings,
                   splits, CTA bands)
     .ev-rv-group  the block's direct children stagger in (card grids,
                   step rows, stat rails)
     .ev-rv-greet  media (a photo, a figure, a map): a gentle scale-in and
                   fade, on its own or as a child of a group (2026-09-25)

   The motion is a KEYFRAME played by the transient .rv-play class, which
   the driver adds on reveal and removes once it has settled (2026-09-25).
   Nothing stays behind on the element: no transition, no delay, no
   transform. The first version used permanent transitions on .ev-rv and
   on every group child, which replaced the children's own hover
   transitions for good and delayed them by up to .46s after the reveal,
   and an inline transition on a card (photo-link-cards, the homepage
   office tiles) beat it, so those cards popped instead of fading.
   Animations outrank inline declarations; transitions do not.

   Motion budget is deliberately gentle for a medical site: 16px of travel
   (12px for group children), a 0.965 scale for media, 0.55-0.8s ease-out,
   90ms between siblings. It runs once, never on scroll back up. The WP
   driver is assets/js/islands/32-reveal.js (body.ev-motion pages only);
   the prototype's is ui_kits/website/_app.jsx. -------------------------- */
@keyframes ev-rv-rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes ev-rv-rise-sm { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes ev-rv-greet { from { opacity: 0; transform: scale(0.965); } to { opacity: 1; transform: none; } }
@keyframes ev-rv-fade { from { opacity: 0; } to { opacity: 1; } }

.ev-rv.rv-armed { opacity: 0; transform: translateY(16px); }
.ev-rv-group.rv-armed > * { opacity: 0; transform: translateY(12px); }
.ev-rv-greet.rv-armed,
.ev-rv-group.rv-armed > .ev-rv-greet { opacity: 0; transform: scale(0.965); }

.ev-rv.rv-play { animation: ev-rv-rise 0.62s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }
.ev-rv-greet.rv-play { animation: ev-rv-greet 0.8s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; transform-origin: 50% 60%; }
.ev-rv-group.rv-play > * { animation: ev-rv-rise-sm 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }
.ev-rv-group.rv-play > .ev-rv-greet { animation-name: ev-rv-greet; animation-duration: 0.8s; transform-origin: 50% 60%; }
/* --rv-kw: a heading keyword inside a group child wipes in step with that
   child (its stagger delay + the wipe's own .12s), not while it is still
   waiting at opacity 0 (rollout, 2026-09-25). */
.ev-rv-group.rv-play > :nth-child(2) { animation-delay: 0.09s; --rv-kw: 0.21s; }
.ev-rv-group.rv-play > :nth-child(3) { animation-delay: 0.18s; --rv-kw: 0.3s; }
.ev-rv-group.rv-play > :nth-child(4) { animation-delay: 0.27s; --rv-kw: 0.39s; }
.ev-rv-group.rv-play > :nth-child(5) { animation-delay: 0.36s; --rv-kw: 0.48s; }
.ev-rv-group.rv-play > :nth-child(n+6) { animation-delay: 0.42s; --rv-kw: 0.54s; }
/* A group the driver split row by row (2026-09-25): each card is its own
   reveal, and the cards sharing a row cross the observer's line together, so
   .rv-c2 … .rv-c6 (column in the row) carry the group's stagger across it.
   Three classes deep so they beat the .ev-rv.rv-play shorthand wherever it
   sits in the file. */
:is(.ev-rv, .ev-rv-greet).rv-play.rv-c2 { animation-delay: 0.09s; --rv-kw: 0.21s; }
:is(.ev-rv, .ev-rv-greet).rv-play.rv-c3 { animation-delay: 0.18s; --rv-kw: 0.3s; }
:is(.ev-rv, .ev-rv-greet).rv-play.rv-c4 { animation-delay: 0.27s; --rv-kw: 0.39s; }
:is(.ev-rv, .ev-rv-greet).rv-play.rv-c5 { animation-delay: 0.36s; --rv-kw: 0.48s; }
:is(.ev-rv, .ev-rv-greet).rv-play.rv-c6 { animation-delay: 0.42s; --rv-kw: 0.54s; }

/* Horizontal rails fade only: a carousel ([data-ev-rail]) at every width, a
   swipe row (.ev-swipe-row, a grid that becomes a rail) on phones. A rail
   scrolls sideways, so it also clips vertically; a rising card would have
   its foot cut off by its own rail, and most of the row is off to the side
   anyway. */
.ev-rv-group[data-ev-rail].rv-armed > * { transform: none; }
.ev-rv-group[data-ev-rail].rv-play > * { animation-name: ev-rv-fade; }
@media (max-width: 760px) {
  .ev-rv-group.ev-swipe-row.rv-armed > * { transform: none; }
  .ev-rv-group.ev-swipe-row.rv-play > * { animation-name: ev-rv-fade; }
}

/* ---- Gradient-word reveal: ONE mechanism, everywhere ----
   A soft-edged layer in the surrounding text color (currentColor) sits
   over the brand ramp on the same span. The word paints in that color
   from frame 1 — no hidden text, no LCP cost — then the layer slides off
   left → right, line by line, uncovering the gradient. Only
   background-position moves on fixed-size layers, so the edge glides
   without re-rasterizing the ramp. The resting position is the base value,
   so no-animation and reduced-motion both show the plain gradient.

   Three triggers, one look:
     .ev-gx-hero              load-time, the pillar-hub hero keyword
     .ev-motion .ev-vh-title  load-time, the video-hero keyword on the motion
                              pages (a class on the h1 itself, never inside
                              it: headings are TranslatePress translation
                              blocks whose Spanish is stored with their inner
                              markup, so a class added inside one would orphan
                              the translation)
     .rv-anim …               scroll-time, added by the reveal driver only
                              when a block was genuinely revealed (above-
                              the-fold words never animate).

   Scroll-time wipes are HEADING words only (2026-09-25): a gradient word in
   an h1-h4, or in a display line (.ev-h2 / .ev-display) of a revealed unit.
   A revealed block can hold several gradient spans (a heading plus a
   callout, a heading plus a stat), and sweeping them all at once reads as
   busy on a medical page; one keyword per section is the calm version.
   The driver enforces it: a revealed unit holding two or more heading
   keywords (a row of columns each with a gradient verb, twin headings)
   plays without .rv-anim, so they rest as printed (rollout, 2026-09-25). */
@keyframes ev-gx-wipe {
  from { background-position: 100% 0, 0 0; }
  to { background-position: 0% 0, 0 0; }
}
.ev-gx-hero,
.ev-motion :is(.ev-vh-title, .ev-arrive-kw) :is(.ev-gradient-text, .ev-gradient-text-light),
.rv-anim :is(h1, h2, h3, h4) :is(.ev-gradient-text, .ev-gradient-text-light),
.rv-anim :is(h1, h2, h3, h4):is(.ev-gradient-text, .ev-gradient-text-light),
:is(h1, h2, h3, h4).rv-anim :is(.ev-gradient-text, .ev-gradient-text-light),
.ev-rv.rv-anim :is(.ev-h2, .ev-display) :is(.ev-gradient-text, .ev-gradient-text-light),
.ev-rv.rv-anim :is(.ev-h2, .ev-display):is(.ev-gradient-text, .ev-gradient-text-light) {
  background-image:
    linear-gradient(90deg, transparent 0%, transparent 42%, currentColor 58%, currentColor 100%),
    var(--gx-ramp, var(--gradient-brand));
  background-size: 240% 100%, 100% 100%;
  background-position: 0% 0, 0 0;
  background-repeat: no-repeat;
}
.ev-gx-hero { animation: ev-gx-wipe 1.35s cubic-bezier(0.65, 0, 0.25, 1) 0s both; }

/* ---- Pillar-hub hero: the proof column slides in from the right ----
   Load-time, alongside the headline wipe. Each row fades and slides in
   18px from the right, 160ms after the one above it (--i is set per row
   in the markup). Secondary content only — the h1 is never touched, so
   the LCP paint is unaffected. Reduced motion shows the rows in place. */
@keyframes ev-hero-pt-in {
  from { opacity: 0; transform: translateX(18px); }
  to { opacity: 1; transform: none; }
}
.ev-hero-pt { animation: ev-hero-pt-in 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) calc(var(--i, 0) * 160ms) both; }

/* ---- Pillar-hub hero: the proof column's width ----
   Lifted out of the inline style because a breakpoint cannot be written inline,
   and an inline declaration would beat this rule anyway.

   Above the breakpoint 33% is the design — the column sits exactly on that cap
   from a 900px row upward. Below it, 33% is the bug: the column drops onto its
   own line and then still refuses to use it, so every hairline stops short. At
   a 457px viewport the bars measured 280px in a 409px row, 129px of nothing
   beside them.

   flex-basis:100% rather than width:100% does two jobs at once. It fills the
   line, and it GUARANTEES the stack, because a 100% basis cannot share a line
   with the headline. min-width drops to 0 so the 280px floor cannot overflow a
   phone narrower than 328px.

   WHY 840 AND NOT 816.
   The row wraps on its own at a container width of exactly 768px: 440px of
   headline flex-basis + the 48px gap + this column's 280px min-width. Measured,
   not derived — 767 stacks, 768 does not. The section pads 24px a side, so the
   naive breakpoint is 768 + 48 = 816.

   That is wrong, and measurably so. A media query matches window.innerWidth,
   which INCLUDES the classic scrollbar; the row is laid out inside
   documentElement.clientWidth, which EXCLUDES it. At an 816px viewport the
   container is only 801px — the row had already stacked while the query had not
   yet fired, leaving 280px of bars in a 753px row with 473px empty. The first
   version of this rule shipped exactly that band, one scrollbar wide, and the
   width sweep is what caught it.

   840 closes it for any scrollbar up to 24 CSS px. Below the query the stack is
   forced and full-width; above it the container is at least 840 - 24 = 816px,
   comfortably wider than the 768px the row needs, so it never wraps on its own
   and no stacked-but-narrow state can exist between the two. The cost is that
   on overlay-scrollbar platforms (macOS, mobile) stacking begins up to 24px
   earlier than it strictly must, which nobody can see. A band of viewports
   where two thirds of the hero is empty, they can.

   verify-staging.mjs -> checkHeroStatsBreakpoint re-derives that 816 on every
   run from the four numbers that produce it — the wrapper's side padding, the
   row's gap, the headline's flex-basis and this column's min-width, all read
   off the live page and stylesheet — and fails if the query stops clearing it,
   or clears it by less than a scrollbar. Change any one of those four and the
   check will tell you this number has to move with them. */
.ev-hero-stats { flex: 0 1 auto; max-width: 33%; min-width: 280px; }
@media (max-width: 840px) {
  .ev-hero-stats { flex-basis: 100%; max-width: 100%; min-width: 0; }
}
.rv-anim :is(h1, h2, h3, h4) :is(.ev-gradient-text, .ev-gradient-text-light),
.rv-anim :is(h1, h2, h3, h4):is(.ev-gradient-text, .ev-gradient-text-light),
:is(h1, h2, h3, h4).rv-anim :is(.ev-gradient-text, .ev-gradient-text-light),
.ev-rv.rv-anim :is(.ev-h2, .ev-display) :is(.ev-gradient-text, .ev-gradient-text-light),
.ev-rv.rv-anim :is(.ev-h2, .ev-display):is(.ev-gradient-text, .ev-gradient-text-light) { animation: ev-gx-wipe 1s cubic-bezier(0.65, 0, 0.25, 1) var(--rv-kw, 0.12s) backwards; }
.ev-motion :is(.ev-vh-title, .ev-arrive-kw) :is(.ev-gradient-text, .ev-gradient-text-light) { animation: ev-gx-wipe 1.35s cubic-bezier(0.65, 0, 0.25, 1) 0s both; }

/* ---- Arrival greetings on the motion pages (2026-09-25) ----
   Load-time, first screen only, and only on body.ev-motion (inc/motion.php).
   The scroll reveal never touches a hero, so these give the top of each pilot
   page its own quiet arrival, sequenced after the headline's entrance:
     homepage   the four tiles settle up into place, one after another
     knee hub   the banner photo eases from a hair larger into its frame; the
                press row fades in after the proof rows
     GAE page   the keyword wipes (above), the video frame settles
   Transform only wherever the element might be the LCP (the homepage tiles
   hold the LCP image, the hub banner and the GAE frame straddle the fold):
   Chrome drops opacity:0 elements as LCP candidates, and a fade on an
   ancestor of the Vascular tile's mix-blend layer would isolate the blend.
   Fill mode 'backwards' throughout, so nothing is pinned once it has played
   (the tiles' own inline transforms and hover transitions take over again).
   The book tile keeps its background drift: one comma list, because a
   separate rule here would replace ev-bookshift (tokens/layout.css). */
@keyframes ev-arrive-settle { from { transform: translateY(14px); } to { transform: none; } }
@keyframes ev-arrive-ease-in { from { transform: scale(1.025); } to { transform: none; } }
@keyframes ev-arrive-frame { from { transform: scale(0.985); } to { transform: none; } }
@keyframes ev-arrive-fade { from { opacity: 0; } to { opacity: 1; } }
/* Homepage tiles, revised the same day at the practice's request ("staggering
   in, but not too delayed"): each tile fades up from just below and a hair
   smaller, 90ms after the one before, all four in place within about a
   second. The fade is safe for the LCP here, measured: the LCP is the Vein
   Care (desktop) or Knee Pain (phone) photo at ~1s, bound by the image
   download, and the tiles have finished fading before it arrives. */
@keyframes ev-tile-in { from { opacity: 0; transform: translateY(22px) scale(0.985); } to { opacity: 1; transform: none; } }
.ev-motion .ev-hero-bigtiles > a { animation: ev-tile-in 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) 0.08s backwards; }
.ev-motion .ev-hero-bigtiles > a + a { animation-delay: 0.17s; }
.ev-motion .ev-hero-bottomrow > a { animation: ev-tile-in 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) 0.26s backwards; }
.ev-motion .ev-hero-bottomrow > .ev-book-tile { animation: ev-tile-in 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) 0.35s backwards, ev-bookshift 9s ease-in-out infinite alternate; }

/* ---- Homepage hero tiles: hover, focus and the pain pulse (2026-09-25) ----
   The prototype (home.jsx) lifted a tile on hover and pulsed a red "pain"
   layer over the sore spot, but that hover script was never ported, so both
   red layers sat invisible behind a TODO. It is CSS now:
     • hover or keyboard focus: the tile rises 6px onto a deeper shadow, its
       photo eases forward (scaled from the feet up, both photo layers
       together so the red stays on the sore spot), the arrow nudges, and on
       the two big tiles the pain layer brightens to full strength;
     • always: the pain layer pulses over the sore spot, continuously
       (practice, 2026-09-25: "the red pulsing should be playing
       infinitely"; the first version pulsed twice and rested). It fades in
       a beat after the tiles land, then pulses at three-quarter strength;
       hover or focus brings it to full strength. Phones, which have no
       hover, see it too.
   One pulse animation runs on the image the whole time and hover only
   changes the wrapper's opacity, so hovering never restarts the pulse or
   makes it jump.
   The tiles' inline styles (verbatim from the prototype) set transform:none
   and their own transitions, hence the !important on the hover values.
   Hover effects apply only where the device can hover; keyboard focus gets
   the same lift everywhere. */
.ev-home-hero .ev-pain { position: absolute; inset: 0; z-index: 0; pointer-events: none; opacity: 0.75; transition: opacity 0.5s var(--ease-out); animation: ev-pain-in 0.8s ease 1.05s backwards; }
@keyframes ev-pain-in { from { opacity: 0; } }
.ev-home-hero .ev-pain > img { animation: ev-redpulse 1.6s ease-in-out infinite; }
.ev-hero-bigtiles > a, .ev-hero-bottomrow > a { transition: transform 0.35s var(--ease-out), box-shadow 0.35s var(--ease-out), background var(--duration) !important; }
.ev-hero-bigtiles > a img { transform-origin: 50% 100% !important; }
.ev-vasc-bg { transform-origin: 70% 50%; transition: filter 0.4s var(--ease-out), transform 0.6s var(--ease-out) !important; }
:is(.ev-hero-bigtiles, .ev-hero-bottomrow) > a:focus-visible { transform: translateY(-6px) !important; box-shadow: 0 30px 56px -20px rgba(16, 4, 28, 0.8), 0 12px 24px -12px rgba(16, 4, 28, 0.5) !important; }
.ev-hero-bigtiles > a:focus-visible img { transform: scale(1.045) !important; }
.ev-hero-bigtiles > a:focus-visible .ev-pain { opacity: 1; }
:is(.ev-hero-bigtiles, .ev-hero-bottomrow) > a:focus-visible .ev-tile-arrow { transform: translateX(4px) !important; }
.ev-hero-bottomrow > a:focus-visible .ev-vasc-bg { filter: brightness(0.78) saturate(1) !important; transform: scale(1.05); }
@media (hover: hover) {
  :is(.ev-hero-bigtiles, .ev-hero-bottomrow) > a:hover { transform: translateY(-6px) !important; box-shadow: 0 30px 56px -20px rgba(16, 4, 28, 0.8), 0 12px 24px -12px rgba(16, 4, 28, 0.5) !important; }
  .ev-hero-bigtiles > a:hover img { transform: scale(1.045) !important; }
  .ev-hero-bigtiles > a:hover .ev-pain { opacity: 1; }
  :is(.ev-hero-bigtiles, .ev-hero-bottomrow) > a:hover .ev-tile-arrow { transform: translateX(4px) !important; }
  .ev-hero-bottomrow > a:hover .ev-vasc-bg { filter: brightness(0.78) saturate(1) !important; transform: scale(1.05); }
}
@media (prefers-reduced-motion: reduce) {
  /* Still acknowledged: the shadow deepens and the red layer shows on hover or
     focus, but nothing moves or pulses, and the red stays hidden at rest
     rather than flashing on and off. */
  :is(.ev-hero-bigtiles, .ev-hero-bottomrow) > a:is(:hover, :focus-visible),
  :is(.ev-hero-bigtiles, .ev-hero-bottomrow) > a:is(:hover, :focus-visible) :is(img, .ev-tile-arrow, .ev-vasc-bg) { transform: none !important; }
  .ev-home-hero .ev-pain, .ev-home-hero .ev-pain > img { animation: none !important; }
  .ev-home-hero .ev-pain { opacity: 0; }
}
.ev-motion .ev-ph-banner { animation: ev-arrive-ease-in 1.6s cubic-bezier(0.22, 0.61, 0.36, 1) 0.1s backwards; }
.ev-motion .ev-ph-press { animation: ev-arrive-fade 0.6s ease 0.5s backwards; }
.ev-motion .ev-vh-frame { animation: ev-arrive-frame 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) 0.15s backwards; }

/* The arthritic column's tint band in the knee-hub contrast table arrives a
   beat after the table, so the eye settles on the problem side. Its text is
   dark on light, so it reads the same before the tint lands. */
.ev-rv.rv-play .ev-cct-band { animation: ev-rv-fade 0.5s ease 0.35s backwards; }

/* ---- Arrival hooks for every hero (2026-09-25 rollout) ----
   The site has a dozen hero blocks, so rather than one rule per hero the
   blocks put these classes on their own elements (never inside a heading),
   and each plays once on load, on body.ev-motion pages only:
     .ev-arrive-kw    on the h1's own tag: its gradient keyword wipes in
                      (the text paints from frame 1, so it is LCP-safe)
     .ev-arrive-ease  a hero photo inside a clipping frame eases from a hair
                      larger into place (transform only: it may be the LCP)
     .ev-arrive-lift  settles up 14px (transform only: for anything that may
                      be the LCP, or that carries a blend or backdrop layer)
     .ev-arrive-rise  fades up 16px: SECONDARY hero content only (stat rows,
                      proof points, trust strips), never the h1, the lead
                      paragraph or the main photo
     .ev-arrive-fade  fades in: a press or logo row under the hero
     .ev-arrive-d1/-d2/-d3  stagger steps (.25 / .4 / .55s) for the above
   The scroll reveal (islands/32-reveal.js) never touches the first section,
   so these are the hero's only motion. */
.ev-motion .ev-arrive-ease { animation: ev-arrive-ease-in 1.6s cubic-bezier(0.22, 0.61, 0.36, 1) var(--arrive-delay, 0.1s) backwards; }
.ev-motion .ev-arrive-lift { animation: ev-arrive-settle 0.75s cubic-bezier(0.22, 0.61, 0.36, 1) var(--arrive-delay, 0.15s) backwards; }
.ev-motion .ev-arrive-rise { animation: ev-rv-rise 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) var(--arrive-delay, 0.2s) backwards; }
.ev-motion .ev-arrive-fade { animation: ev-arrive-fade 0.6s ease var(--arrive-delay, 0.45s) backwards; }
.ev-arrive-d1 { --arrive-delay: 0.25s; }
.ev-arrive-d2 { --arrive-delay: 0.4s; }
.ev-arrive-d3 { --arrive-delay: 0.55s; }
/* Keyboard focus inside a fading arrival settles it at once: the Media page's
   press rail is a tab stop, and a quick Tab after the skip link reached it at
   opacity 0 during its fade (review, 2026-09-25). */
.ev-motion :is(.ev-arrive-fade, .ev-arrive-rise, .ev-ph-press):focus-within { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .ev-gx-hero, .ev-hero-pt,
  .ev-motion :is(.ev-vh-title, .ev-arrive-kw) :is(.ev-gradient-text, .ev-gradient-text-light),
  .rv-anim :is(.ev-gradient-text, .ev-gradient-text-light),
  .rv-anim:is(.ev-gradient-text, .ev-gradient-text-light),
  .ev-motion .ev-hero-bigtiles > a, .ev-motion .ev-hero-bottomrow > a,
  .ev-motion .ev-ph-banner, .ev-motion .ev-ph-press, .ev-motion .ev-vh-frame,
  .ev-motion :is(.ev-arrive-ease, .ev-arrive-lift, .ev-arrive-rise, .ev-arrive-fade) { animation: none !important; }
}

/* Anyone who turns reduced motion on mid-visit, or prints, gets the finished
   page: nothing armed stays hidden and nothing mid-play is captured. */
@media (prefers-reduced-motion: reduce) {
  .ev-rv.rv-armed, .ev-rv-greet.rv-armed, .ev-rv-group.rv-armed > * { opacity: 1 !important; transform: none !important; }
  .rv-play, .rv-play > *, .rv-play .ev-cct-band { animation: none !important; }
}
@media print {
  .ev-rv.rv-armed, .ev-rv-greet.rv-armed, .ev-rv-group.rv-armed > * { opacity: 1 !important; transform: none !important; }
  .rv-play, .rv-play > *, .rv-play .ev-cct-band, .rv-anim :is(.ev-gradient-text, .ev-gradient-text-light) { animation: none !important; }
  .ev-motion :is(.ev-arrive-ease, .ev-arrive-lift, .ev-arrive-rise, .ev-arrive-fade, .ev-ph-banner, .ev-ph-press, .ev-vh-frame) { animation: none !important; }
  .ev-gae:not(.is-in) .ev-gae-step, .ev-gae:not(.is-in) .ev-gae-connector { opacity: 1 !important; transform: none !important; }
  .ev-gae-step, .ev-gae-letter, .ev-gae-connector { transition: none !important; }
}

/* ====== tokens/presets.css =============================================== */
/* ============================================================
   Empire Vein & Vascular — Text & surface presets.
   The ONE place type roles are defined. Pages add a role class and keep
   only layout (margin, max-width, text-align) inline. Sizes come from the
   documented scale in typography.css; nothing here invents a new number.
   ============================================================ */
:root {
  /* Heading sizes: fluid clamps, one per role.
     --h1-min is lifted out of its clamp because the MIN *is* the phone size:
     5vw only overtakes 2.4rem at a 768px viewport, so every screen below that
     renders the min verbatim. Naming it lets the phone rule below move that
     end alone, with the 5vw growth and the 4rem desktop ceiling still written
     once. The other four roles keep literal mins — nothing asked them to move. */
  --h1-min:     2.4rem;
  --h1-size:    clamp(var(--h1-min), 5vw, 4rem); /* pillar / page hero */
  --h1-sm-size: clamp(2.3rem, 4.5vw, 3.4rem);  /* compact hero (quiz, locations, hubs) */
  --h2-size:    clamp(2.1rem, 4vw, 3rem);      /* section heading */
  --h2-sm-size: clamp(1.9rem, 3.4vw, 2.6rem);  /* secondary section heading */
  --h3-size:    clamp(1.7rem, 3vw, 2.3rem);    /* feature sub-heading */
}

/* ── Phone: give the subpage hero h1 its weight back ─────────────────────
   38.4px over an 18px lead is a 2.13x step — flat for the first thing on the
   page, so the headline stopped carrying it. 43.2px over the 17px phone lead
   (typography.css nudges --text-lg in the same breath) reads as 2.54x.

   Scoped to 640px rather than edited into the clamp min directly: --h1-size
   resolves to its min all the way up to 768px, so raising that min
   unconditionally would have grown tablet portrait too. 640px is the phone
   boundary base.css already uses for .ev-btn wrapping and .ev-stage-pairs,
   and it clears 768px with room.

   Only --h1-size moves. --h1-sm-size is not a subpage-hero-only token: it also
   sizes the homepage hero tiles ("Vein Care" / "Knee Pain"), tuned separately
   against --ev-tile-*, plus a half-dozen section h2s. .ev-h1 is heroes and the
   evidence-stats numeral, and that numeral sits one per row on a phone with a
   full column to grow into. */
@media (max-width: 640px) {
  :root { --h1-min: 2.7rem; }  /* 38.4px → 43.2px */

  /* The COMPACT hero needs the same lift, and --h1-sm-size cannot give it: as
     the note above says, that token also sizes the homepage hero tiles and a
     half-dozen section h2s, so moving it would inflate all of those too.

     Selecting by TAG separates them exactly — this class sits on eight h1s
     (hero headlines) and eight h2s (section heads), and an h1 carrying it is a
     page hero by definition. /locations/ is the page this is really for: it
     measured 36.8px over a 17px lead, a 2.16x step, which is the same flat
     relationship the practice asked us to fix on the other subpages. 2.55rem
     is 40.8px — a 2.4x step, and deliberately under .ev-h1's 43.2px, because
     this is the compact hero and should still read as the smaller of the two. */
  h1.ev-h1-sm { font-size: 2.55rem; }
}

/* ── Headings ───────────────────────────────────────────────────────── */
.ev-h1, .ev-h1-sm, .ev-h2, .ev-h2-sm, .ev-h3 {
  font-weight: var(--weight-extrabold);
  color: var(--text-heading);
  margin: 0;
  text-wrap: balance;
}
.ev-h1    { font-size: var(--h1-size);    letter-spacing: var(--tracking-tighter); line-height: 1.03; }
.ev-h1-sm { font-size: var(--h1-sm-size); letter-spacing: -0.035em;                line-height: 1.05; }
.ev-h2    { font-size: var(--h2-size);    letter-spacing: var(--tracking-tighter); line-height: 1.06; }
.ev-h2-sm { font-size: var(--h2-sm-size); letter-spacing: var(--tracking-tighter); line-height: 1.08; }
.ev-h3    { font-size: var(--h3-size);    letter-spacing: -0.025em;                line-height: 1.1;  }
/* A gradient word that also carries a heading role must NOT take the
   heading ink: its fill is transparent anyway, and its `color` is what the
   reveal wipe starts from (currentColor) — so it keeps inheriting the
   surrounding text color, white on a plum band, ink on a light one. */
.ev-gradient-text, .ev-gradient-text-light { color: inherit; }

/* ── Copy ───────────────────────────────────────────────────────────── */
.ev-lead { font-size: var(--text-lg);   line-height: 1.6;  color: var(--text-body); margin: 0; text-wrap: pretty; }
.ev-body { font-size: var(--text-base); line-height: 1.6;  color: var(--text-body); margin: 0; }
.ev-meta { font-size: var(--text-sm);   line-height: 1.5;  color: var(--muted-foreground); margin: 0; }
.ev-fine { font-size: var(--text-xs);   line-height: 1.55; color: var(--muted-foreground); margin: 0; }

/* ====== tokens/layout.css ================================================ */
/* ============================================================
   Empire Vein & Vascular — site layout & responsive rules.
   Was an inline <style> block in index.html; it lives in the token layer
   now so every stylesheet ships through styles.css. Nav overlay anchoring,
   first-hero clearance, the responsive collapses of page grids, the mobile
   swipe row, hero glow keyframes, and the comparison-table phone rules.
   Imported LAST by styles.css so its !important-free overrides still win.
   ============================================================ */

/* The root has to clip sideways too, not just body.
   Several rails bleed to the viewport edge with margin-right: calc(50% - 50vw).
   50vw counts the vertical scrollbar; the visible area does not. On a 375px
   phone with a 15px scrollbar that is ~8px of real, draggable sideways scroll
   on any page carrying one — /about-us/ was doing exactly that. body already
   clipped; html was left visible, so the overflow propagated to the viewport
   anyway.
   clip, not hidden: clip does not create a scroll container, so the sticky
   header (.evw-header) and the sticky About-Us headings keep working. */
html { overflow-x: clip; }

body { margin: 0; background: var(--background); color: var(--text-body); font-family: var(--font-sans); overflow-x: clip; }
#app { min-height: 100vh; display: flex; flex-direction: column; background: var(--background); overflow-x: clip; }
#app > main { flex: 1; }
/* Desktop nav is a transparent overlay (position:absolute) — anchor it to the app box */
#app-inner { position: relative;
  --nav-fg: var(--text-heading);      /* nav links + logo wordmark, over light heroes */
  --nav-fg-soft: var(--text-body);    /* utility-bar text */
  --nav-open: var(--ev-purple-700);   /* an open menu item */
  --nav-divider: rgba(40,20,60,0.14);
}
/* Dark-hero pages (VenaSeal, Varithena, GAE): flip the floating nav to light. */
#app-inner[data-hero-dark="1"] {
  --nav-fg: #fff;
  --nav-fg-soft: rgba(255,255,255,0.82);
  --nav-open: var(--ev-amber-400);
  --nav-divider: rgba(255,255,255,0.20);
}
/* First-hero clearance below the header.
   The MOBILE header (<=1024px) is sticky/in-flow, so each hero clears it via its
   own top padding — nothing needed here. The DESKTOP header (>=1025px) is
   position:fixed (overlay reserving no space): 48px utility bar + 68px nav = 116px.
   We add that 116px as padding-top on the first hero SECTION itself, so it works
   regardless of where the content sits in the DOM — content-first (standard &
   service-hub heroes), content-last (image-bg heroes with an absolute overlay div
   first), or content-then-image-band (service hubs). The section's own background
   fills the added space; absolute overlays anchor to the padding-box top edge and
   are unaffected; each hero's own inline top padding becomes the gap below the bar. */
@media (min-width: 1025px) {
  #main-content > div > section:first-child,
  #main-content > main > section:first-child,
  #main-content > section:first-child,
  #main-content > section.ev-booking-page-section { padding-top: 116px !important; }
}
@media (max-width: 920px) {
  .ev-hero-bigtiles, .ev-hero-bottomrow { grid-template-columns: 1fr !important; }
}
/* Pulsing focal glow on the hero tile images — large, soft, semi-transparent */
.ev-focal { position: absolute; z-index: 1; width: 150px; height: 150px; border-radius: 50%; transform: translate(-50%, -50%); pointer-events: none; background: radial-gradient(circle, rgba(255,45,45,0.55) 0%, rgba(255,45,45,0.32) 38%, rgba(255,45,45,0) 72%); filter: blur(6px); animation: ev-focal-glow 2.4s ease-in-out infinite; }
@keyframes ev-focal-glow { 0%, 100% { transform: translate(-50%, -50%) scale(0.82); opacity: 0.55; } 50% { transform: translate(-50%, -50%) scale(1.12); opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) { .ev-focal { animation: none; } }
@keyframes ev-redpulse { 0%, 100% { opacity: 0.15; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .ev-hero-bigtiles img { animation: none !important; } }
/* Book Appointment tile — very slow purple↔orange color drift */
.ev-book-tile { background: linear-gradient(120deg, #7a1fa6 0%, #a63fae 40%, var(--ev-orange-dark) 100%); background-size: 220% 220%; animation: ev-bookshift 9s ease-in-out infinite alternate; }
@keyframes ev-bookshift { 0% { background-position: 0% 50%; } 100% { background-position: 100% 50%; } }
@media (prefers-reduced-motion: reduce) { .ev-book-tile { animation: none; } }
/* Home below-hero (dark plum comp) responsive collapse */
.ev-review-rail { -ms-overflow-style: none; }
.ev-review-rail::-webkit-scrollbar { display: none; }
@media (max-width: 980px) {
  .ev-team-grid, .ev-testi-grid { grid-template-columns: 1fr !important; margin-top: 0 !important; }
  .ev-priority-grid, .ev-loc-grid { grid-template-columns: 1fr 1fr !important; }
}
@media (max-width: 680px) {
  .ev-priority-grid, .ev-loc-grid { grid-template-columns: 1fr !important; }
}
/* Reviews / testimonial card grid — fills the desktop width for 3–4 cards without
   leaving a lone stretched card (4-up desktop → 2-up tablet → 1-up mobile). */
.ev-reviews-grid { display: grid; gap: 24px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); }
@media (max-width: 1040px) and (min-width: 621px) { .ev-reviews-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .ev-reviews-grid { grid-template-columns: 1fr; } }
/* Footer decorative legs — large-desktop peek only (hidden once masthead narrows) */
@media (max-width: 900px) { .ev-foot-legs { display: none !important; } }
/* Standalone booking hero uses the shared first-hero clearance rule above. */
@media (max-width: 760px) {
  .vv-hero-grid { grid-template-columns: 1fr !important; }
  .vv-hero-grid > .vv-hero-media { min-height: 300px !important; order: -1; }
  .vv-info-grid { grid-template-columns: 1fr !important; }
  /* AAA "what is it" grid: stack in DOM order (text → body → pair) */
  .aaa-whatis-grid { grid-template-columns: 1fr !important; }
  .aaa-whatis-grid > * { grid-column: auto !important; grid-row: auto !important; }
  /* Big-numeral diagnosis steps: pin the numeral to the top of its row */
  .ev-monostep-big { align-items: start !important; }
  .koa-tier { grid-template-columns: 1fr !important; gap: 14px !important; }
  .gvk-quick { grid-template-columns: 1fr 1fr !important; }
  .gvk-quick > * { border-left: none !important; }
  .gvk-steps-head { grid-template-columns: 1fr !important; align-items: start !important; }
  .gvk-step-row { display: flex !important; flex-direction: column; align-items: flex-start; gap: 14px !important; padding: 30px 0 !important; }
  .gvk-step-num { font-size: 2.8rem !important; }
  .gvk-testi { grid-template-columns: 1fr !important; }
  .gvk-hero-scrim { background: linear-gradient(90deg, rgba(28,10,42,0.94) 0%, rgba(28,10,42,0.82) 60%, rgba(28,10,42,0.55) 100%) !important; }
  .gvk-hero { grid-template-columns: 1fr !important; }
  .gvk-hero-media { order: -1; }
  .gvk-row { grid-template-columns: 1fr !important; }
  .gvk-row > * { order: 0 !important; }
  .vv-info-divider { display: none !important; }
  .vr-flow-grid, .vr-risk-grid, .vr-dx-grid, .vv-reflux-grid { grid-template-columns: 1fr !important; }
  .lu-progression { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .ev-ba-grid { grid-template-columns: 1fr !important; }
  .sc-iac-band { grid-template-columns: 1fr !important; text-align: center; }
  .ev-story-toggle { grid-template-columns: 1fr !important; }
  .vv-spider-callout { flex-direction: column !important; align-items: flex-start !important; }
  .gvk-fda-badge { position: static !important; right: auto !important; bottom: auto !important; margin-top: 28px !important; }
  .ev-foot-mast { grid-template-columns: 1fr !important; gap: 28px !important; }
  .ev-foot-cols { grid-template-columns: 1fr 1fr !important; gap: 32px !important; }

  /* ── Mobile swipe row ─────────────────────────────────────────────
     Turns a card grid into a horizontal, snap-scrolling carousel on
     phones. Add `ev-swipe-row` alongside whatever grid classes/inline
     styles the element already has — the desktop layout is untouched,
     because every rule here lives inside this max-width query.

     Two things make the affordance obvious without extra chrome:
     cards are sized under one full width (default 78%) so the NEXT
     card is always part-visible, and the row bleeds out to both screen
     edges so that peek reads as "more to the right" rather than as a
     clipping bug. Per-instance tuning:
       --ev-swipe-card   width of each card   (default 78%)
       --ev-swipe-bleed  side padding to cancel (defaults to
                         --section-pad-x, which IS .ev-section's padding —
                         a wider bleed scrolls the whole page sideways) */
  .ev-swipe-row {
    display: flex !important;
    grid-template-columns: none !important;
    /* A wrapping rail is just a stack that also hides its scrollbar. */
    flex-wrap: nowrap !important;
    /*
     * justify-content:center on a scrollport puts the overflow on BOTH sides,
     * and the left half is unreachable: scrollLeft is already 0 there, so there
     * is nothing left to scroll back to. Measured on treatment-cards, whose
     * wrapper centres inline: the first of five cards sat at -488px, two cards
     * invisible with no way to reach them. design.css already fixes exactly this
     * for .ev-faq-tabs and says why; a rail needs the same guarantee.
     */
    justify-content: flex-start !important;
    /*
     * A fallback gutter, deliberately NOT !important so any block that sets its
     * own gap inline keeps it. Blocks built on .ev-divider-grid have none: they
     * separate columns with per-cell padding and a border-left hairline, and the
     * phone rules strip both. numbered-steps then rendered its step cards
     * butting flush together, measured 0px between all three.
     */
    gap: 24px;
    overflow-x: auto;
    /* proximity, not mandatory: mandatory forbids resting between cards, so a
       flick gets re-aimed at the next edge and the row pages one card at a time
       instead of scrolling. */
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    margin-inline: calc(-1 * var(--ev-swipe-bleed, var(--section-pad-x))) !important;
    padding-inline: var(--ev-swipe-bleed, var(--section-pad-x));
    scroll-padding-inline-start: var(--ev-swipe-bleed, var(--section-pad-x));
    /* Vertical breathing room: overflow-x turns the block axis into a
       scrollport too, which would otherwise crop each card's shadow. */
    padding-block: 4px 12px;
    scrollbar-width: none;
  }
  .ev-swipe-row::-webkit-scrollbar { display: none; }
  .ev-swipe-row > * {
    /*
     * !important because some blocks size their own flex children INLINE, and
     * an inline style beats any class. treatment-cards ships
     * style="flex:1 1 330px" on each card and flex-wrap:wrap on the row, so the
     * rail applied, reported display:flex and overflow-x:auto, and still did
     * nothing: the cards wrapped back into a column and scrollWidth equalled
     * clientWidth. It measured as a rail and behaved as a stack.
     */
    flex: 0 0 var(--ev-swipe-card, 78%) !important;
    min-width: 0;
    scroll-snap-align: start;
  }

  /* ── Rails vs. the divider treatment ───────────────────────────────
     .ev-divider-grid stacks its cells and rules a hairline ABOVE each one,
     with !important, as the phone form of the vertical rules it draws on
     desktop. That is right for a stacked column and wrong the moment the
     same row becomes a rail: the cards sit side by side and the hairline
     draws a line across the top of every card. The blocks affected are the
     ones built on ev-divider-grid — icon-columns and numbered-steps.
     Two classes deep, so this wins on specificity regardless of order. */
  .ev-swipe-row.ev-divider-grid > * {
    border-top: none !important;
    padding-top: 0 !important;
    margin-top: 0;
  }

  /* Per-block peek widths. The default 78% suits a card with a heading and
     a line or two; these need their own. Never 100% — the peek IS the
     affordance, and a full-width card removes it. */
  /* Eight dense comparison cards: a little wider so each one is readable
     without the row turning into sixteen swipes. */
  .ev-rail--treatment-compare-grid { --ev-swipe-card: 84%; }
  /* Stage photos read as a filmstrip; more of them on screen helps the
     progression land. */
  .ev-rail--vein-progression { --ev-swipe-card: 62%; }
  .ev-rail--stage-progression { --ev-swipe-card: 62%; }

  /* A rail is focusable (see evvs_rail_attrs) so it can be scrolled from a
     keyboard. Make that focus visible — without this the ring is invisible
     against the card edges and the affordance is keyboard-only in theory. */
  .ev-swipe-row:focus-visible {
    outline: var(--focus-outline);
    outline-offset: -2px;
    border-radius: var(--radius-lg);
  }
}
/* GAE vs. Knee Replacement comparison table — keeps both columns on
   phones (the GAE / Knee Replacement headings have to keep describing
   the cells under them), so type, padding, and the ✓/✕ marks tighten. */
@media (max-width: 560px) {
  .gvk-cmp th { font-size: 11.5px !important; padding: 11px 11px !important; }
  .gvk-cmp td { padding: 12px 11px !important; }
  .gvk-cmp td span { font-size: 13.5px !important; }
  .gvk-cmp-cc { gap: 8px !important; }
  .gvk-cmp-ic { width: 19px !important; height: 19px !important; }
  .gvk-cmp-ic svg { width: 12px !important; height: 12px !important; }
}
.gvk-fda-badge { position: absolute; right: clamp(20px, 4vw, 40px); bottom: clamp(30px, 4vw, 44px); }
@keyframes ev-arrow-dash { to { stroke-dashoffset: 0; } }
@keyframes ev-arrow-fade { to { opacity: 1; } }
.ev-arrow-doodle .ev-arrow-stem { stroke-dasharray: 100; stroke-dashoffset: 100; animation: ev-arrow-dash 1.05s cubic-bezier(.6,.05,.25,1) .45s forwards; }
.ev-arrow-doodle .ev-arrow-head { opacity: 0; animation: ev-arrow-fade .35s ease 1.45s forwards; }
.ev-arrow-note { opacity: 0; animation: ev-arrow-fade .4s ease .25s forwards; }
@media (prefers-reduced-motion: reduce) {
  .ev-arrow-doodle .ev-arrow-stem { animation: none; stroke-dashoffset: 0; }
  .ev-arrow-doodle .ev-arrow-head { animation: none; opacity: 1; }
  .ev-arrow-note { animation: none; opacity: 1; }
}
@media (max-width: 900px) { .ev-arrow-doodle, .ev-arrow-note { display: none !important; } }

/* ── The doctor-beside-FAQ split (doctor-faq block) ─────────────────────
   Two rules that have to move together, which is why they are their own
   query rather than living in the 760px block with the other gvk-* ones.

   THE STICKY BUG. .gvk-doc-card is position:sticky;top:96px inline, which is
   right while the card is a sidebar beside a tall FAQ column. Once the grid
   stacks, the card is still sticky — so it pins itself 96px below the header
   and the FAQ list, which paints no background of its own, scrolls straight
   over the photograph. Measured at 652px and 375px: card and list rectangles
   genuinely intersect, and the doctor shows through the questions.
   Stacking must therefore switch the card back to static.

   THE BREAKPOINT. Stacking moved 760px -> 900px. At 768px the split gave the
   photo 340px and the FAQ text 321px, so the reading column was NARROWER than
   the portrait beside it — the tablet case the client described as looking
   odd. Below 900px the questions get the full width instead. */
@media (max-width: 900px) {
  .gvk-faq { grid-template-columns: 1fr !important; }
  .gvk-doc-card {
    position: static !important;
    max-width: 360px;
    margin: 0 auto;
  }
}

/* ── The press logo strip (inc/press-strip.php) ─────────────────────────
   Shared by the homepage band and the pillar heroes. Desktop is one centred
   wrapping row, label inline with the logos.

   The PILL is the load-bearing part: several press marks are dark artwork on
   transparency and cannot be recoloured, so they need white behind them to be
   readable on any ground. The knee hub shipped without it and its logos were
   nearly invisible on the lavender page. */
.ev-press {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 16px clamp(20px, 3vw, 40px);
}
.ev-press-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.ev-press-logos {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 16px clamp(20px, 3vw, 40px);
}
.ev-press-pill {
  display: inline-flex; align-items: center; justify-content: center;
  height: 52px; padding: 0 16px;
  background: var(--ev-white); border-radius: var(--radius-lg);
  box-shadow: 0 8px 18px -10px rgba(0, 0, 0, 0.5);
}
/* ── RAIL mode: the always-scrolling carousel ─────────────────────────
   One horizontal row at every width, bigger pills, drag to scroll. This is
   the treatment the press page has always used; About Us now shares it.
   The pill is the same element as in wrap mode, just larger, so the two
   presentations cannot drift apart the way the four hand-written copies
   of this strip did. */
.ev-press--rail { display: block; text-align: center; }
.ev-press--rail .ev-press-label { display: block; margin-bottom: 18px; letter-spacing: 0.12em; color: var(--muted-foreground); }
.ev-press--rail .ev-press-pill {
  height: 64px; padding: 0 22px;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}
.ev-press--rail .ev-press-pill img { height: 40px; max-width: 120px; }
/* nowrap is load-bearing. The base .ev-press-logos wraps — correct for the
   centred row it was written for, and fatal here: a rail that wraps is a grid
   that also hides its scrollbar. Folding the press page markup into this
   shared renderer inherited that wrap and silently turned its carousel into a
   two-row block; measured scrollWidth === clientWidth with ten pills on a
   375px screen, which is only possible if they wrapped.

   safe center: centred while the row fits, left-aligned once it overflows, so
   the first logo never sits out of reach to the left of scrollLeft:0. */
.ev-press--rail .ev-press-logos {
  flex-wrap: nowrap;
  justify-content: safe center;
}

.ev-press-pill img {
  height: 34px; width: auto; max-width: 104px;
  object-fit: contain; display: block;
}

@media (max-width: 760px) {
  /* Label on its own line, logos on one rail beneath it. Six logos otherwise
     wrap into three ragged rows. */
  /* width:100% because on the homepage this is a flex ITEM sitting beside the
     IAC seal; without it the block shrinks to its content and the rail cannot
     span the screen. */
  .ev-press { display: block; width: 100%; text-align: center; }
  .ev-press-label { display: block; margin-bottom: 14px; }
  /* 31% shows three whole pills and most of a fourth on a 375px screen, so the
     row reads as continuing. Card rails use 78%; a logo is not a card. */
  /*
   * No bleed override: both callers now pad by --section-pad-x, which is what
   * .ev-swipe-row already cancels by default.
   *
   * A viewport-relative bleed — calc(50% - 50vw) — was tried first and is
   * wrong here: 100vw counts the scrollbar, so the rail overshot 7px and cut
   * the first pill at scrollLeft:0, on both pages. Making the two containers
   * agree on one token fixes it exactly instead of approximately.
   *
   * 29% is measured, not guessed. At 33% exactly three pills fitted and the
   * fourth began past the edge — three whole logos and NO peek, which reads as
   * a row that ends. 29% puts about a third of the fourth pill on screen, so
   * the row visibly continues. Card rails use 78%; a logo is not a card.
   */
  .ev-rail--press { --ev-swipe-card: 29%; gap: 10px; }
  .ev-press-pill { height: 46px; padding: 0 10px; }
  .ev-press-pill img { height: 28px; max-width: 100%; }
}
