/*
 * hv-elementor.css — Elementor-side brand glue. Layout grammar lives in the
 * Elementor recipes; this file owns: inline-copy utilities the motion layer
 * depends on, and all page-section / content styling.
 *
 * Chrome rules (header/footer) have moved to hv-global.css (unconditionally
 * enqueued) so the chrome is self-sufficient regardless of which widget CSS
 * Elementor conditionally loads on a given page (lesson 40 / 2026-06-21).
 *
 * The contact-modal (.hv-modal*) stays here — it is a separate component,
 * not chrome. Values reference --hv-* tokens (assets/css/hv-tokens.css).
 */

:root {
  --hv-accent-green-rgb: 63, 122, 42; /* --hv-accent-dark #3F7A2A as channels for rgba() */
}

/* ─── Screen-reader-only utility (.sr-only) ─────────────────────────────────
   Clip-path pattern: visually hides content while keeping it in the
   accessibility tree (visible to screen readers, not to sighted users).
   Use on populated headings that establish a valid outline for a section that
   has no visible heading — e.g. a section whose only child headings are h3s
   needs an sr-only h2 ancestor so axe evaluates h1 → h2 → h3, not h1 → h3.
   Differs from display:none (removes from a11y tree) and opacity:0
   (leaves a paint gap). The 1px × 1px box with clip-path inset(50%) reduces
   to a zero-area rectangle, invisible and non-interactive. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Eyebrow pill — precedes the hero H1; hv-motion folds it into hero-lines.
   .hv-eyebrow lands on the .elementor-element wrapper (lesson 13). Applying
   width:fit-content here prevents the pill from spanning the full column
   width when the wrapper is in a flex column. The pill visuals stay on the
   wrapper itself (inline-block would be overridden by the flex item layout,
   so fit-content + block is the reliable fix at the wrapper level). */
.hv-eyebrow {
  display: block;
  width: fit-content;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--hv-accent-dark);
  background: transparent;
  border: 1px solid rgba(var(--hv-accent-green-rgb), 0.35);
  border-radius: 999px;
  padding: 6px 16px;
  margin-bottom: 14px;
  line-height: 1.4;
}

/* Eyebrow→heading gap is owned solely by the eyebrow's margin-bottom. */
.hv-eyebrow + h1,
.hv-eyebrow + h2 {
  margin-top: 0;
}

/* Two-tone heading accent — <em class="hv-accent-phrase"> wraps ONE key phrase.
   Green is the authored final state; hv-motion tweens ink→green under no-preference. */
.hv-accent-phrase {
  color: var(--hv-accent-dark);
  font-style: italic;
  font-weight: inherit;
}

/* Worked-numbers display numeral — the count-up effect's target. */
.hv-numeral-display {
  font-family: var(--hv-font-display);
  font-size: clamp(4rem, 6vw, 6rem);
  line-height: 0.95;
  font-weight: 500;
  color: var(--hv-primary);
}

/* Trust / feature chips — horizontal wrapping flow.
   .hv-chips on the icon-list .elementor-element wrapper; the actual
   list element is .elementor-icon-list-items inside it.
   icon-list layout:"inline" sets inline on individual items but the
   list container itself stays block. This rule makes it flex+wrap. */
.hv-chips .elementor-icon-list-items {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Chrome rules (header/footer, utility bar, nav, sticky collapse, logo, mobile
   header, drawer, dropdown, footer links) moved to hv-global.css (2026-06-21).
   Nothing from this block remains here — see hv-global.css for all chrome. */

/* ─── Bridge-link widget: anchor colour override ─────────────────────────────
   The kit's global anchor style resolves to ink (hv_foreground). The bridge-link
   text-editor widget binds hv_reading_foreground on the widget text_color (prose
   grey for the lead-in sentence), but the <a> anchor inside it must read in
   hv-link / hv-link-hover. This rule overrides the kit anchor default for that
   widget only. .hv-bridge-link lands on the .elementor-element wrapper.
   L3 (2026-06-17): weight 600 so the arrow-link reads as an actionable text-button.
   2026-07-05: also matches `a.hv-bridge-link` -- the class applied DIRECTLY on a
   hand-authored raw <a> tag embedded inside a shared body widget (e.g. the
   reputation hero soft-step, which also needs hv-survey-modal-open
   and so can't use markdown link syntax), where no ancestor carries the class. */
.hv-bridge-link a,
a.hv-bridge-link {
  color: var(--hv-link);
  font-weight: 600;
  transition: color 0.2s ease;
}
.hv-bridge-link a:hover,
a.hv-bridge-link:hover {
  color: var(--hv-link-hover);
}

/* ─── Credential emphasis — quiet secondary-deep weight ──────────────────────
   Used in the ai-value proof band credential line only. Secondary-deep (#0E3F6A)
   gives the certification phrase a touch more weight than the surrounding ink body
   without using the hero green (.hv-accent-phrase) which is reserved for H1 devices. */
.hv-credential-emphasis {
  color: var(--hv-secondary-deep);
  font-style: normal;
  font-weight: inherit;
}

/* ─── Card-foot: pin the last widget to the bottom of a flex-column card ────
   Used by shelf_pair, card_grid arrow-link widgets.  The .elementor-element
   wrapper is a flex item in the card's flex-column container; margin-top:auto
   pushes it to the foot regardless of body length, so paired cards' links align.
   L3 (2026-06-17): anchor weight 600 + link colour so micro-CTAs read as actionable. */
.hv-card-foot {
  margin-top: auto !important;
}
.hv-card-foot a {
  color: var(--hv-link);
  font-weight: 600;
  transition: color 0.2s ease;
}
.hv-card-foot a:hover {
  color: var(--hv-link-hover);
}

/* ─── Clickable card: dedicated cover-anchor widget (2026-06-20, v2) ────────────
   Replaces the broken ::after-on-static-foot approach (v1, 2026-06-20).
   Root cause of v1 failure: .hv-card-foot a is position:static, so its ::after's
   containing block resolved to the nearest positioned ancestor — the .hv-card-foot
   widget div (~51px tall), not the ~400px card container. The cover never stretched
   beyond the foot strip.

   Fix: a dedicated text_editor widget (.hv-card-cover-widget) injected as the last
   child of each card container. The widget's .elementor-element wrapper is set
   position:absolute; inset:0; z-index:3, so it uses the card container (already
   position:relative in Elementor's optimised-markup build) as its containing block
   and stretches to the full card. The <a> inside fills the widget via height:100%.
   The visible footer link (.hv-card-foot) sits under the cover (z-index:2) and
   provides the visible affordance; the cover provides the click + accessible name.
   Card body text becomes non-selectable (cover sits on top) — accepted trade-off.
   LIFT hover-rise is unchanged (supplied by CARD-SYSTEM; cover is a static link).
   Reduced-motion guard: the hover-rise is guarded in CARD-SYSTEM; the cover anchor
   itself needs no guard (it is a static positioned element, no animation). */

/* Card containers need overflow:hidden so the absolute cover respects border-radius */
#solutions-clusters > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#home-journeys > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#products-shelf > .e-con-boxed > .e-con-inner > .e-con > .e-con {
  overflow: hidden;
}

/* The cover-anchor widget: absolute, full-card, above content (z-index 3) */
.hv-card-cover-widget {
  position: absolute !important;
  inset: 0 !important;
  z-index: 3 !important;
  margin-top: 0 !important;
  padding: 0 !important;
}
.hv-card-cover-widget .elementor-widget-container,
.hv-card-cover-widget .elementor-text-editor,
.hv-card-cover-widget p {
  height: 100%;
  margin: 0;
  padding: 0;
}
.hv-card-cover-widget a {
  display: block;
  height: 100%;
  text-decoration: none;
  color: transparent;
  font-size: 0;
}

/* >>> CARD-SYSTEM (generated by recipes/card_css.py) >>> */
/* CARD SYSTEM — generated; edit CARD_SECTIONS in recipes/card_css.py, not here.
   Keyed on section _element_ids + container nesting (container classes don't
   render — lesson 33). A section absent from the table keeps its bordered flat
   default (safe). Three tiers: SHADOW (resting), LIFT (clickable hover),
   REGION (CSS subgrid alignment, desktop/tablet ≥768px). */

/* ── Resting surface: soft shadow + no border (all cards) ── */
#home-journeys > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#home-pillars > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con,
#solutions-clusters > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#products-shelf > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#partners-ladder > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#partners-models > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#missed-calls-contrast > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con,
#one-system-contrast > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con,
#data-reporting-contrast > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con,
#ai-value-contrast > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con,
#keeping-running-contrast > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con,
#custom-apps-contrast > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con,
#missed-calls-how > .e-con-boxed > .e-con-inner > .e-con,
#custom-apps-how > .e-con-boxed > .e-con-inner > .e-con,
#data-reporting-how > .e-con-boxed > .e-con-inner > .e-con,
#ai-value-how > .e-con-boxed > .e-con-inner > .e-con,
#reputation-price > .e-con-boxed > .e-con-inner > .e-con {
  box-shadow: var(--hv-shadow-card) !important;
  border: 0 !important;
}

/* ── (1) LIFT hover — clickable cards only ── */
#home-journeys > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#solutions-clusters > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#products-shelf > .e-con-boxed > .e-con-inner > .e-con > .e-con {
  transition: transform 0.25s ease, box-shadow 0.25s ease !important;
}
#home-journeys > .e-con-boxed > .e-con-inner > .e-con > .e-con:hover,
#solutions-clusters > .e-con-boxed > .e-con-inner > .e-con > .e-con:hover,
#products-shelf > .e-con-boxed > .e-con-inner > .e-con > .e-con:hover {
  transform: translateY(-6px) !important;
  box-shadow: 0 18px 40px rgba(31, 41, 55, 0.16) !important;
}
@media (prefers-reduced-motion: reduce) {
#home-journeys > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#solutions-clusters > .e-con-boxed > .e-con-inner > .e-con > .e-con,
#products-shelf > .e-con-boxed > .e-con-inner > .e-con > .e-con { transition: none !important; }
#home-journeys > .e-con-boxed > .e-con-inner > .e-con > .e-con:hover,
#solutions-clusters > .e-con-boxed > .e-con-inner > .e-con > .e-con:hover,
#products-shelf > .e-con-boxed > .e-con-inner > .e-con > .e-con:hover { transform: none !important; }
}

/* ── (3) REGION ALIGNMENT — subgrid per section (desktop/tablet) ── */
@media (min-width: 768px) {
  /* home-journeys — 3-col, auto 1fr auto */
  #home-journeys > .e-con-boxed > .e-con-inner > .e-con {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto 1fr auto;
    column-gap: 24px;
  }
  #home-journeys > .e-con-boxed > .e-con-inner > .e-con > .e-con {
    display: grid !important; grid-template-rows: subgrid; grid-row: span 3; row-gap: 12px;
  }
  /* home-pillars — 2-col, auto 1fr */
  #home-pillars > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) {
    display: grid !important;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: auto 1fr;
    column-gap: 32px;
  }
  #home-pillars > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con {
    display: grid !important; grid-template-rows: subgrid; grid-row: span 2; row-gap: 16px;
  }
  /* products-shelf — row products-shelf-row-1, 2-col, auto auto 1fr auto auto */
  #products-shelf-row-1 {
    display: grid !important;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: auto auto 1fr auto auto;
    column-gap: 24px;
  }
  #products-shelf-row-1 > .e-con {
    display: grid !important; grid-template-rows: subgrid; grid-row: span 5; row-gap: 12px;
  }
  /* products-shelf — row products-shelf-row-2, 3-col, auto auto 1fr auto auto */
  #products-shelf-row-2 {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto auto 1fr auto auto;
    column-gap: 24px;
  }
  #products-shelf-row-2 > .e-con {
    display: grid !important; grid-template-rows: subgrid; grid-row: span 5; row-gap: 12px;
  }
  /* partners-ladder — 3-col, auto auto 1fr */
  #partners-ladder > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto auto 1fr;
    column-gap: 24px;
  }
  #partners-ladder > .e-con-boxed > .e-con-inner > .e-con:has(> .e-con) > .e-con {
    display: grid !important; grid-template-rows: subgrid; grid-row: span 3; row-gap: 12px;
  }
  /* partners-models — 2-col, auto auto 1fr */
  #partners-models > .e-con-boxed > .e-con-inner > .e-con {
    display: grid !important;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: auto auto 1fr;
    column-gap: 24px;
  }
  #partners-models > .e-con-boxed > .e-con-inner > .e-con > .e-con {
    display: grid !important; grid-template-rows: subgrid; grid-row: span 3; row-gap: 16px;
  }
}
/* <<< CARD-SYSTEM <<< */


/* ── (1) LIFT: resting shadow for insights article cards (xpro-post-grid) ── */
/* The xpro-post-grid widget renders .xpro-post-grid-item cards; the section
   container itself is #insights-articles. We add shadow + brand overrides here.
   The card items are NOT .e-con children (they are the widget's own DOM), so
   the CARD-SYSTEM .e-con selector chain does not reach them — target directly. */
#insights-articles .xpro-post-grid-item {
  box-shadow: var(--hv-shadow-card) !important;
  border: 0 !important;
  border-radius: 8px;
  overflow: hidden;
  background: var(--hv-card);
  transition: transform 0.25s ease, box-shadow 0.25s ease !important;
}
#insights-articles .xpro-post-grid-item:hover {
  transform: translateY(-6px) !important;
  box-shadow: 0 18px 40px rgba(31, 41, 55, 0.16) !important;
}
@media (prefers-reduced-motion: reduce) {
  #insights-articles .xpro-post-grid-item { transition: none !important; }
  #insights-articles .xpro-post-grid-item:hover { transform: none !important; }
}

/* Xpro post-grid brand theming: category tag, title, excerpt, read-more */
#insights-articles .xpro-post-grid-item .xpro-post-grid-cat a,
#insights-articles .xpro-post-grid-item .xpro-post-grid-category a {
  color: var(--hv-primary-deep) !important;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
#insights-articles .xpro-post-grid-item .xpro-post-grid-title,
#insights-articles .xpro-post-grid-item .xpro-post-grid-title a {
  color: var(--hv-foreground) !important;
  font-family: var(--hv-font-display);
  font-size: clamp(1.1rem, 1.6vw, 1.25rem);
  font-weight: 500;
  line-height: 1.3;
}
#insights-articles .xpro-post-grid-item .xpro-post-grid-title a:hover {
  color: var(--hv-primary) !important;
}
#insights-articles .xpro-post-grid-item .xpro-post-grid-content,
#insights-articles .xpro-post-grid-item .xpro-post-grid-excerpt {
  color: var(--hv-reading-foreground) !important;
  font-size: 0.9375rem;
}
#insights-articles .xpro-post-grid-item .xpro-post-grid-readmore a,
#insights-articles .xpro-post-grid-item .xpro-post-grid-btn a {
  color: var(--hv-link) !important;
  font-size: 0.875rem;
  font-weight: 500;
  transition: color 0.2s ease;
}
#insights-articles .xpro-post-grid-item .xpro-post-grid-readmore a:hover,
#insights-articles .xpro-post-grid-item .xpro-post-grid-btn a:hover {
  color: var(--hv-link-hover) !important;
}

/* ─── Site-wide contact form modal (#hv-modal-contact) ─────────────────────
   Theme-level modal shell injected via wp_footer on every page (functions.php).
   Hidden by display:none so the iframe leaves the render tree when closed —
   the GHL modal iframe click-block fix is NOT needed (package note, §5 BUILD).
   Any <a href="#hv-modal-contact"> triggers open it; JS binds all triggers. */

.hv-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 99999;
  align-items: center;
  justify-content: center;
  background: rgba(31, 41, 55, 0.6);
}

.hv-modal.is-open {
  display: flex;
}

.hv-modal__inner {
  position: relative;
  /* border-box so max-height bounds the OUTER box (padding included). With
     content-box the 96px padding pushed the outer box past the viewport, so a
     ~620px GHL form was clipped by the screen edges on laptop-height windows.
     Asymmetric padding keeps 48px at the top to clear the absolute close button
     while trimming the rest to give the form more room before it must scroll. */
  box-sizing: border-box;
  background: var(--hv-card);
  border-radius: 12px;
  padding: 48px 40px 40px;
  width: min(640px, calc(100vw - 48px));
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  box-shadow: 0 24px 60px rgba(31, 41, 55, 0.2);
}

/* Compact icon close control. The Elementor Global Kit styles ALL <button>s as
   full primary CTAs (.elementor-kit-204 button — padding:16px 32px, primary bg,
   radius 4px) with enough weight to beat plain specificity here, so the close
   button's sizing + colour are forced with !important (the established pattern
   for beating the kit). Without this it renders as a large green CTA. */
.hv-modal .hv-modal__close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px !important;
  height: 32px !important;
  padding: 0 !important;
  background: var(--hv-card) !important;
  border: 1px solid var(--hv-border);
  border-radius: 50% !important;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--hv-muted-foreground) !important;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(31, 41, 55, 0.12);
  transition: color 0.2s ease, background 0.2s ease;
}

.hv-modal .hv-modal__close:hover {
  color: var(--hv-foreground) !important;
  background: var(--hv-muted) !important;
}

/* Contact-modal triggers (a.elementor-button[href="#hv-modal-contact"]) flip to
   dark orange after the modal closes: closeModal() returns focus to the trigger
   (correct for keyboard a11y) and the Elementor Kit groups button :focus WITH
   :hover (.elementor-kit-204 .elementor-button:focus → --e-global-color-hv_primary_dark,
   #C07208), with no per-widget :focus override, so the kit orange beats the green
   resting state on focus. These CTAs rest on the accent green (#245418 =
   var(--hv-accent), verified live: rgb(36,84,24)).

   Note we can't gate on :focus-visible here: Chrome applies :focus-visible when
   focus is returned PROGRAMMATICALLY after a dialog closes, so a mouse-driven
   close still matches :focus-visible — an earlier :not(:focus-visible) attempt
   was silently skipped and the orange persisted. So: hold the resting green on
   ALL focus (a genuine :hover still wins its darker-green hover), and give
   keyboard users a conventional outline ring instead of a background change. */
a[href="#hv-modal-contact"].elementor-button:focus:not(:hover) {
  background-color: var(--hv-accent) !important;
}
a[href="#hv-modal-contact"].elementor-button:focus-visible {
  outline: 2px solid var(--hv-foreground) !important;
  outline-offset: 2px;
}

/* Prevent body scroll while modal is open */
body.hv-modal-open {
  overflow: hidden;
}

/* ─── hv-survey-modal — reusable GHL survey modal component (2026-07-05) ───
   Theme-level component (task: Reviews & Reputation §5 -- "Get set up" opens
   the SV-SA1 onboarding survey in an on-site modal instead of navigating).
   Reuses body.hv-modal-open above for the scroll-lock; markup injected via
   functions.php hvel_survey_modal_reviews() (wp_footer, is_page-scoped);
   open/close/resize wired by assets/js/hv-survey-modal.js.

   Gotcha (a) baked in: display:none when closed -- the iframe can never sit
   invisibly over the page eating clicks (reference_ghl_modal_iframe_click_block).

   Starting sizes (TUNE LIVE on staging with ?hv-debug=1 -- flagged
   NEEDS-LIVE-QA, wp-builder is browserless): desktop panel max-width 900px /
   max-height 85vh; iframe min-height 480px (form_embed.js drives the real
   height via postMessage once visible -- gotcha b, hv-survey-modal.js
   re-nudges window.iFrameResize() on open). Mobile (≤767px): full-screen sheet,
   100dvh, edge-to-edge, slim top bar. */

.hv-survey-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 99999;
  align-items: center;
  justify-content: center;
  background: rgba(31, 41, 55, 0.6);
}

.hv-survey-modal.is-open {
  display: flex;
}

.hv-survey-modal__inner {
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  background: var(--hv-card);
  border-radius: 12px;
  width: min(900px, calc(100vw - 48px));
  max-height: 92vh;
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(31, 41, 55, 0.2);
  /* Calm fade/scale-in on open (task: "quick calm fade/scale-in"). Display
     flips instantly (not transitionable) so there is no fade-OUT on close --
     accepted, matches the existing .hv-modal's no-animation baseline. */
  transform: scale(0.96);
  opacity: 0;
  transition: transform 0.22s ease, opacity 0.22s ease;
}

.hv-survey-modal.is-open .hv-survey-modal__inner {
  transform: scale(1);
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .hv-survey-modal__inner {
    transition: none;
  }
}

/* Close control floats over the top-right corner rather than taking a flex row,
   so the survey iframe uses the panel's full height (the GHL survey is a fixed
   ~850px; on short windows every reclaimed pixel reduces the internal scroll).
   The ✕ sits over the survey's own top padding, so it covers no field. */
.hv-survey-modal__bar {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 2;
  display: flex;
  justify-content: flex-end;
  padding: 8px;
}

/* Compact icon close control -- same beat-the-kit pattern as .hv-modal__close
   (the Global Kit styles ALL <button>s as full primary CTAs; !important forces
   the small round icon button through). */
.hv-survey-modal__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px !important;
  height: 32px !important;
  padding: 0 !important;
  background: var(--hv-card) !important;
  border: 1px solid var(--hv-border);
  border-radius: 50% !important;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--hv-muted-foreground) !important;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(31, 41, 55, 0.12);
  transition: color 0.2s ease, background 0.2s ease;
}

.hv-survey-modal__close:hover {
  color: var(--hv-foreground) !important;
  background: var(--hv-muted) !important;
}

.hv-survey-modal__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0 16px 12px;
}

.hv-survey-modal__body iframe {
  display: block;
  width: 100%;
  min-height: 480px;   /* fallback floor; form_embed.js sets the real height */
  border: none;
}

/* Mobile (≤767px): full-screen sheet, edge-to-edge, slim top bar.
   Size the panel as 100% of the fixed inset:0 container rather than 100vw/100dvh:
   under device-mode DPR / the page's viewport-meta, vw/dvh resolved to a smaller
   box than the container's inset:0, so the panel rendered offset (top-right,
   part-way down) instead of full-screen. 100% tracks the container exactly. */
@media (max-width: 767px) {
  .hv-survey-modal__inner {
    width: 100%;
    height: 100%;
    max-height: none;
    border-radius: 0;
  }
  .hv-survey-modal__bar {
    padding: 8px 12px;
  }
  .hv-survey-modal__body {
    padding: 0 12px 24px;
  }
}

/* Legal prose — line-height override for dense legal text (1.6 per design spec).
   .hv-legal-body lands on text-editor widgets inside the legal prose column.
   .hv-legal-updated is the muted "Last updated" line — no extra rule needed,
   handled entirely by the hv_muted_foreground / hv_body_sm kit bindings. */
.hv-legal-body .elementor-widget-text-editor,
.hv-legal-body p,
.hv-legal-body li {
  line-height: 1.6;
}

/* ─── Two-pillar orbital figure (About §2) ──────────────────────────────────
   Custom theme-level component (README hard constraints — exception #1): a figure
   that can't be expressed with native Elementor widgets. The about recipe drops a
   native marker container (#hv-orbital) in the story's inner row; hv-orbital.js
   renders the rings/pills/core + GSAP motion into it. The two-column row + the
   responsive stack are owned HERE, keyed on the section/marker _element_id (lesson
   33: container _css_classes don't render; ids do). Calm-over-loud motion lives in
   the JS; reduced-motion / editor → static ring; no-JS → empty seat collapses. */
#about-story .e-con-inner { align-items: center; }
#about-story .e-con-inner > .e-con:not(#hv-orbital) { flex: 1 1 auto; max-width: 560px; }
#about-story #hv-orbital { flex: 0 0 660px; width: 660px; }
#about-story #hv-orbital:empty { display: none; }      /* no-JS / pre-render: prose owns the row */
#about-story #hv-orbital .hv-orb-stage { will-change: transform; }

/* Stack the figure below the prose on tablet/mobile; scale it to the viewport
   (hv-orbital.js builds its box from the seat's clientWidth, so this is all it needs). */
@media (max-width: 1199px) {
  #about-story .e-con-inner { flex-direction: column; align-items: center; }
  #about-story .e-con-inner > .e-con:not(#hv-orbital) { max-width: 100%; }
  #about-story #hv-orbital { flex: 0 0 auto; width: min(660px, 90vw); margin-top: 24px; }
}

/* ─── Insights single-post template (SINGLE-POST-DESIGN.md §§ Elementor-native) ──
   Scoped under #hv-article (the template's root container _element_id — lesson 33:
   container IDs render as DOM id, unlike _css_classes which don't render on
   containers). Nothing in this block leaks to recipe pages.

   Architecture: posts 35/36/37 are now full Elementor documents (not Gutenberg).
   Each post body is a boxed 1320px container (matching normal pages) with native
   Elementor heading + text-editor widgets. The xpro-post-content widget in band (C)
   of template 252 is bypassed for Elementor posts — Elementor renders the post's
   own _elementor_data instead. The old Gutenberg measure/alignfull rules below are
   retired; the new rules style the Elementor article-body widgets. */

/* Featured image band: full-width within the template, brand radius + aspect.
   The Xpro featured-image widget is displayed inline; when no featured image is
   set it renders no <img> so the band collapses via :has(img) guard below. */
#hv-article-image .hv-article-featured-img {
  width: 100%;
  max-width: 480px;
  margin-bottom: var(--hv-space-8, 32px);
}
#hv-article-image .hv-article-featured-img img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--hv-radius-lg, 8px);
  display: block;
}

/* Empty-safe: the xpro-featured-image widget renders no <img> when the post has
   no thumbnail. Collapse the whole image band so no gap or broken box appears.
   The band reappears automatically once a featured image is set. */
#hv-article-image:not(:has(img)) {
  display: none;
}

/* S1 fix (2026-06-17): the figure empty-safe rule must be ID-keyed, not class-
   keyed. Container _css_classes don't render (lesson 33), so .hv-figure:not(:has(img))
   never fires. The build script sets _element_id="hv-fig-{id}" on every figure
   container, which DOES render as the DOM id — use that as the selector. */
[id^="hv-fig-"]:not(:has(img)) {
  display: none;
}

/* Meta line: category in primary-deep uppercase caps (mirrors the post-grid
   category tag treatment); separators + date + reading time in muted. */
.hv-post-meta {
  display: block;
  line-height: 1.6;
}
.hv-meta-category {
  color: var(--hv-primary-deep);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color 0.2s ease;
}
.hv-meta-category:hover {
  color: var(--hv-primary);
}
/* Insights post-grid category — the Xpro post-grid widget's own meta class
   (folder icon + category link), distinct from the single-post .hv-meta-category
   above. Xpro's default grey #929292 (~3.0:1 on cream) fails WCAG AA; mirror the
   single-post treatment (primary-deep, 7.2:1) on both the icon and the link. */
.xpro-post-grid-meta-category,
.xpro-post-grid-meta-category a,
.xpro-post-grid-meta-category i {
  color: var(--hv-primary-deep) !important;
}
.xpro-post-grid-meta-category a:hover {
  color: var(--hv-primary) !important;
}
.hv-meta-sep {
  color: var(--hv-border);
  margin-inline: 2px;
}
.hv-meta-date,
.hv-meta-read,
.hv-meta-author {
  color: var(--hv-muted-foreground);
}

/* Article title (H1). Size is left to the kit hv_display_h1 global (same as page
   heroes) — reverted 2026-06-15 per Jon. .hv-article-title only owns the gap before
   the body prose: it's the sole source of that spacing (the band container has no
   flex gap), so this value IS the visible gap. !important beats the framework
   widget-margin reset; kept below the 40px H2 section breaks so hierarchy holds. */
.hv-article-title {
  margin-bottom: 20px !important;
}

/* Article body prose widget (.hv-article-prose on the text-editor .elementor-element).
   H2 headings in the rich-text body: kit display family, foreground colour, generous
   spacing above. Body paragraphs: 1.7 line-height, normal flow. Closing link:
   hv-link colour + hover. These override browser/kit defaults only inside
   .hv-article-prose so nothing leaks to recipe pages. */
.hv-article-prose .elementor-widget-text-editor {
  line-height: 1.7;
}
.hv-article-prose h2 {
  font-family: var(--hv-font-display);
  color: var(--hv-foreground);
  margin-top: 2.5rem;
  margin-bottom: 1rem;
  line-height: 1.25;
}
.hv-article-prose p {
  margin-top: 0;
  margin-bottom: 1.25rem;
}
.hv-article-prose a {
  color: var(--hv-link);
  transition: color 0.2s ease;
}
.hv-article-prose a:hover {
  color: var(--hv-link-hover);
}

/* Article footer band vertical padding step-down on mobile (SINGLE-POST-DESIGN.md §4). */
@media (max-width: 767px) {
  #hv-article #hv-article-meta {
    padding-top: 32px;
  }
}

/* Scroll colour-flip sticky nav block moved to hv-global.css (2026-06-21). */

/* ─── LBGS price table: styled pricing comparison ───────────────────────────
   The de_risk Variant C outcome carries a native <table> (label column +
   Starter / Growth / Pro). Default table rendering reads crowded + basic; this
   gives it breathing room, a clear hierarchy (plan names > prices > the Best-for
   descriptor), row-separator rules only (no spreadsheet gridlines), and a calm
   warm highlight on the Growth column — the plan where the AI Receptionist
   becomes included. Scoped to the section _element_id #lbgs-price (lesson 33). */
#lbgs-price table {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}
#lbgs-price th,
#lbgs-price td {
  padding: 16px 24px;
  text-align: center;
  border: 0;
  border-bottom: 1px solid var(--hv-border);
  vertical-align: middle;
}
/* label column (Set-up / Monthly / AI Receptionist / Best for): left, muted */
#lbgs-price thead th:first-child,
#lbgs-price tbody td:first-child {
  text-align: left;
  color: var(--hv-muted-foreground);
  font-weight: 500;
  white-space: nowrap;
}
/* plan-name header row: display, bold, strong baseline rule */
#lbgs-price thead th {
  font-family: var(--hv-font-display);
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--hv-foreground);
  border-bottom: 2px solid var(--hv-foreground);
  padding-bottom: 14px;
}
/* price figures (Set-up + Monthly rows): the focal point */
#lbgs-price tbody tr:nth-child(1) td:not(:first-child),
#lbgs-price tbody tr:nth-child(2) td:not(:first-child) {
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--hv-foreground);
}
/* Best for row (last): italic descriptor, breathing room above, no baseline rule */
#lbgs-price tbody tr:last-child td {
  font-style: italic;
  font-size: 0.9rem;
  color: var(--hv-muted-foreground);
  border-bottom: 0;
  padding-top: 18px;
}
/* Growth column (3rd col): calm warm highlight + accent cap */
#lbgs-price th:nth-child(3),
#lbgs-price td:nth-child(3) {
  background: var(--hv-background-warm);
}
#lbgs-price thead th:nth-child(3) {
  border-top: 3px solid var(--hv-accent);
  border-radius: var(--hv-radius-md) var(--hv-radius-md) 0 0;
}

/* horizontal scroll on mobile: the 4-column table overflows its card on narrow
   screens — let it scroll rather than break the layout. */
@media (max-width: 767px) {
  #lbgs-price {
    overflow-x: auto;
  }
}

/* ─── Editorial illustration image widgets (content-media convention) ──────────
   All image widgets whose binding class ends in "-media" (e.g.
   hv-k-home-hero-media, hv-k-ar-hero-media, hv-k-missed-calls-problem-media)
   live inside a flex-column inner container (width_pc=45 or 55, align-items=
   flex-start). Without an explicit width, the widget wrapper gets flex:0 1 auto
   and collapses to its img's intrinsic size — which is 0 pre-load because the
   img has no width/height attributes and loading="lazy" needs a non-zero
   intersection area to trigger. Result: permanent 0×0, image never loads.

   Fix: the binding class lands on the .elementor-element wrapper (widget, so
   lesson 33 doesn't apply), so target it directly. stretch + width:100% makes
   the wrapper fill its column width and provides a non-zero box for the lazy
   intersection check. `display:block` on the img itself stops the inline-element
   default bottom gap.

   Scope: `.elementor-widget-image[class*="-media"]` matches only image widgets
   whose element carries a class containing "-media". Does NOT match:
   - header/footer chrome (none end in -media)
   - Insights post figures (class is hv-k-post-*-fig-* hv-figure__img)
   - mermaid SVG images (also hv-figure__img)
   - the orbital/mermaid native containers (containers, not widget-image)
   So the decision-tree SVG on /insights/low-code-or-custom-software/ is safe.

   Regression point: the recipe has always emitted these without width_pc; the
   image was briefly working in early sessions because the page was opened and
   re-saved in the builder (which serialises a 100% width inline style), but
   that in-builder save was never recorded in any recipe or CSS rule. This is the
   permanent source-level fix so a rebuild keeps it. */

.elementor-widget-image[class*="-media"] {
  width: 100%;
  align-self: stretch;
}

.elementor-widget-image[class*="-media"] img {
  width: 100%;
  height: auto;
  display: block;
}

/* ─── Hero below-copy illustration band (#home-hero hero_image_url, 2026-06-19) ─
   The image widget is pinned to hero_image_band_width px via _element_custom_width
   (lesson 26) and centred in its band container (align-items:center on the parent).
   The img renders at full widget width, height auto (aspect-ratio preserved) --
   effectively contain for a landscape illustration. Border-radius via the house
   card token so it sits in the cream band with the same corner feel as cards.
   max-width:100% guards narrow viewports (the 1000px pin applies ≥1000px only). */
.elementor-widget-image[class*="-hero-image"] {
  max-width: 100%;
}
.elementor-widget-image[class*="-hero-image"] img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--hv-radius-lg, 12px);
}

/* ─── Phone-arc signature positioning (#hv-phone-arcs, missed-calls §1) ───────
   Custom theme-level component (README hard constraints — exception #2).
   The right column of §1 carries _element_id="hv-phone-arcs"; hv-phone-arcs.js
   renders the SVG arc overlay into it. The column itself and the contained <img>
   are native Elementor widgets (recipe); this block only sizes the arc canvas. */

#hv-phone-arcs {
  position: relative;
}

#hv-phone-arcs .hv-arc-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

/* ─── Insights: inline citation superscripts ─────────────────────────────────
   a.hv-cite is rendered by build_posts.py from [^id] markers in the prose.
   Superscript size + weight; inherits hv-link colour. scroll-margin on each
   <li id="src-*"> so jump-to clears the sticky header. */
a.hv-cite {
  font-size: 0.7em;
  vertical-align: super;
  font-weight: 600;
  color: var(--hv-link);
  text-decoration: none;
  transition: color 0.2s ease;
}
a.hv-cite:hover {
  color: var(--hv-link-hover);
}

/* ─── Insights: figure block (.hv-figure) ────────────────────────────────────
   Centred at max 760px within the 1320px body band; generous breathing room;
   brand radius; NO shadow/border (charts carry their own visual weight).
   Empty-safe: handled by [id^="hv-fig-"]:not(:has(img)) (S1 fix — see below
   the #hv-article-image rule; lesson 33: container _css_classes don't render). */
.hv-figure {
  max-width: 760px;
  margin: 48px auto;
  border-radius: var(--hv-radius-md, 6px);
}
.hv-figure__img img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--hv-radius-md, 6px);
}
.hv-figure__caption .elementor-widget-text-editor {
  text-align: center;
}
.hv-figure__caption p {
  font-size: 0.875rem;
  color: var(--hv-muted-foreground);
  margin-top: 12px;
  margin-bottom: 0;
  line-height: 1.5;
}
.hv-figure__source p {
  font-size: 0.8125rem;
  color: var(--hv-muted-foreground);
  margin-top: 4px;
  margin-bottom: 0;
}

/* ─── Insights: Sources list (.hv-article-sources) ──────────────────────────
   End-of-article provenance ledger. Hairline top border separates from prose;
   "Sources" heading is sized below H2 so it reads as a footer note, not a
   section. <ol> at 14px muted. Empty-safe collapse. Each <li id="src-*">
   gets scroll-margin so citation jump-links clear the sticky header. */
.hv-article-sources {
  max-width: 760px;
  border-top: 1px solid var(--hv-border);
  margin-top: 56px;
  padding-top: 24px;
}
.hv-article-sources:not(:has(li)) {
  display: none;
}
.hv-article-sources .hv-sources-heading {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--hv-foreground);
  margin-bottom: 12px;
  margin-top: 0;
}
.hv-article-sources ol {
  font-size: 0.875rem;
  color: var(--hv-muted-foreground);
  line-height: 1.6;
  padding-left: 1.25rem;
  margin: 0;
}
.hv-article-sources ol li {
  margin-bottom: 8px;
}
.hv-article-sources ol li a {
  color: var(--hv-link);
  transition: color 0.2s ease;
}
.hv-article-sources ol li a:hover {
  color: var(--hv-link-hover);
}
/* Each <li id="src-*"> must clear the sticky header on jump-to. */
li[id^="src-"] {
  scroll-margin-top: 96px;
}

/* ─── Insights category archive: H1 from [hv_archive_title] shortcode ───────
   The shortcode is in a text-editor widget (.hv-archive-h1 on the wrapper).
   Text-editor kit typography globals target the widget-level font-size/family.
   Strip browser <h1> default margin-top (text-editor puts the shortcode output
   in a block context that may add top margin). Also suppress the paragraph
   bottom-margin the text-editor's .elementor-widget-container would otherwise
   impose around the <h1> via the browser <p> default cascade. */
.hv-archive-h1 .elementor-widget-text-editor h1.elementor-heading-title,
.hv-archive-h1 h1.elementor-heading-title {
  margin: 0;
  line-height: 1.1;
}
.hv-archive-h1 .elementor-widget-container {
  margin: 0;
}

/* ─── Insights category archive: blurb + target audience ────────────────────
   Output of the [hv_archive_description] shortcode (in a text-editor widget):
   a blurb paragraph then a one-line "Who these are for:" audience note. The
   blurb inherits the intro body type; the audience reads as quiet metadata. */
.hv-archive-blurb p {
  margin: 0 0 0.5em;
}
.hv-archive-blurb p:last-child {
  margin-bottom: 0;
}
.hv-archive-audience {
  margin: 12px 0 0;
  font-size: 0.875em;
  color: var(--hv-muted-foreground);
}
.hv-archive-audience__label {
  font-weight: 600;
  color: var(--hv-reading-foreground);
}

/* ─── G22: Archive journey line [hv_archive_journey] ────────────────────────
   Quiet wayfinding line placed last in the archive intro stack, after the
   audience line. Reads as an inline sentence with a link — same quiet weight as
   .hv-archive-audience (0.875em muted). NOT a button, no pill/box/border.
   Reuses .hv-bridge-link link semantics (colour + weight) scoped to this class.
   :empty collapse: shortcode returns '' for cross-cutting; widget hides cleanly. */
.hv-archive-journey {
  margin: 12px 0 0;
  font-size: 0.875em;
  color: var(--hv-muted-foreground);
}
.hv-archive-journey:empty {
  display: none;
}
.hv-archive-journey a {
  color: var(--hv-link);
  font-weight: 600;
  text-decoration: none;
  transition: color 0.2s ease;
}
.hv-archive-journey a:hover {
  color: var(--hv-link-hover);
}

/* ─── G22: Single-post journey chip (.hv-meta-journey) ──────────────────────
   Placed immediately after .hv-meta-category in the [hv_post_meta] shortcode.
   Visually differentiated: category = plain caps text-link (primary-deep, no box);
   journey chip = muted caps text in a hairline outline box — breadcrumb, NOT a CTA.
   - Same 12px / weight 600 / 0.08em letter-spacing baseline as .hv-meta-category.
   - Hairline 1px hv-border outline, tight 2px 8px padding, small-radius pill.
   - Text hv-muted-foreground (cooler than primary-deep so it can't out-shout category).
   - Hover lifts text to hv-reading-foreground + slightly stronger border. No fill.
   After-care and cross-cutting posts emit no .hv-meta-journey at all (PHP gate). */
.hv-meta-journey {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--hv-muted-foreground) !important;
  border: 1px solid var(--hv-border);
  border-radius: var(--hv--custom--hv--radius--sm, 4px);
  padding: 2px 8px;
  line-height: 1.4;
  transition: color 0.2s ease, border-color 0.2s ease;
  vertical-align: middle;
}
.hv-meta-journey:hover {
  color: var(--hv-reading-foreground) !important;
  border-color: var(--hv-reading-foreground);
}

/* ─── G22: Solutions hub — journey group headers (§2 #solutions-clusters) ───
   Journey group h3: display family at 24px (hv-2xl), one step between the
   section h2 (40px / hv_display_h2 typo global) and the card titles (20px).
   The heading widget binds hv_display_h2 globally; this class overrides to 24px.

   After-care group h3: 20px (hv-xl), matching cluster-card title weight.
   The heading widget binds hv_display_h3 globally; .hv-aftercare-group-heading
   overrides to 20px so the after-care reads quieter than the journey h3s.

   Card titles (now h4): drop from the kit hv_display_h3 (32px) to 20px (hv-xl)
   to sit correctly under their group h3. Scoped to #solutions-clusters so no
   other card_grid section is affected.

   After-care muted overline: .hv-eyebrow-muted — same pill shape as .hv-eyebrow
   but in hv-muted-foreground instead of orange, signalling subordination.

   Journey group intro: constrained to ~62ch reading measure so it stays narrower
   than the full grid width (per DESIGN spec). */
.hv-journey-group-heading .elementor-heading-title {
  font-size: 24px;
  line-height: 1.2;
}
.hv-aftercare-group-heading .elementor-heading-title {
  font-size: 20px;
  line-height: 1.25;
}
#solutions-clusters .hv-solutions-card-title .elementor-heading-title {
  font-size: 20px;
  line-height: 1.25;
}
.hv-journey-group-intro .elementor-widget-text-editor,
.hv-aftercare-group-intro .elementor-widget-text-editor {
  max-width: 62ch;
}
/* After-care divider — hairline above the after-care group, with generous air.
   border-color on the wrapper container doesn't reach Elementor's generated
   .elementor-divider-separator (which carries its own inline border-top-color from
   the widget colour control, defaulting to black). Target the separator directly. */
.hv-aftercare-divider {
  margin-top: 64px;
  margin-bottom: 0;
  opacity: 1;
}
.hv-aftercare-divider .elementor-divider-separator {
  border-top-color: var(--hv-border) !important;
}
/* Muted overline above the after-care h3 — hv-eyebrow shape but muted colour */
.hv-eyebrow-muted {
  display: block;
  width: fit-content;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--hv-muted-foreground);
  background: transparent;
  border: 1px solid var(--hv-border);
  border-radius: 999px;
  padding: 6px 16px;
  margin-bottom: 10px;
  line-height: 1.4;
}

/* ─── Insights category archive: no-posts empty state ───────────────────────
   When a category has zero posts, xpro-post-grid outputs .xpro-alert.xpro-alert-warning.
   Override the Xpro yellow/orange alert with a calm brand-cream treatment.
   Scoped to #insights-archive-articles so it only fires in the archive template. */
#insights-archive-articles .xpro-alert.xpro-alert-warning {
  background: var(--hv-background-warm) !important;
  border: 1px solid var(--hv-border) !important;
  border-radius: 8px;
  padding: 32px 40px;
  color: var(--hv-reading-foreground) !important;
}
#insights-archive-articles .xpro-alert-title {
  display: block;
  font-family: var(--hv-font-display);
  font-size: 1.25rem;
  font-weight: 500;
  color: var(--hv-foreground) !important;
  margin-bottom: 8px;
}
#insights-archive-articles .xpro-alert-description {
  display: block;
  font-size: 1rem;
  color: var(--hv-reading-foreground) !important;
}

/* ─── Insights: prev/next scaffold (#hv-article-prevnext) ───────────────────
   Empty-safe: display:none when no children. Later wave drops nav widgets in.
   When populated: flex space-between (prev left, next right), stack on mobile. */
#hv-article-prevnext:empty {
  display: none;
}
#hv-article-prevnext {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
@media (max-width: 767px) {
  #hv-article-prevnext {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ─── Insights index gap-fills ───────────────────────────────────────────────
   Named empty slots for later waves. Post-grid date muted/small. */

/* Search slot (above category chips, later wave): collapse when empty. */
#hv-insights-search:empty {
  display: none;
}

/* Xpro post-grid card date: muted and smaller (reads as metadata, not body). */
#insights-articles .xpro-post-grid-item .xpro-post-grid-date {
  color: var(--hv-muted-foreground) !important;
  font-size: 0.8125rem;
}

/* ─── Category archive pages (/insights/category/{slug}/) ───────────────────
   Xpro themer template type_archive, conditioned to category|all. The archive
   mirrors the insights index layout (same post-grid, same band colours). The
   dynamic archive title + back-link live in the template's Elementor tree (the
   Xpro Dynamic-Tags Widget outputs the archive title). We style the archive
   heading band and the post-grid (same selectors as #insights-articles, scoped
   to #insights-archive-articles so they coexist). */

/* Category archive: stop the post-grid collapsing to width:0.
   The xpro-post-grid widget (Cube Portfolio / .cbp) positions its cards
   absolutely, so it has no intrinsic content width. Its Elementor container is a
   flex COLUMN with align-items:flex-start, which sizes children to content on the
   cross axis — so the widget shrinks to 0 wide and clips every card (the page
   reads as empty though the markup + thumbnails are all present). Force the widget
   to fill its column. Same flex-collapse class as the editorial-image fix. */
#insights-archive-articles .hv-k-insights-archive-grid {
  width: 100% !important;
  align-self: stretch !important;
}

/* Category archive: reuse the same post-grid card theming as the insights index */
#insights-archive-articles .xpro-post-grid-item {
  box-shadow: var(--hv-shadow-card) !important;
  border: 0 !important;
  border-radius: 8px;
  overflow: hidden;
  background: var(--hv-card);
  transition: transform 0.25s ease, box-shadow 0.25s ease !important;
}
#insights-archive-articles .xpro-post-grid-item:hover {
  transform: translateY(-6px) !important;
  box-shadow: 0 18px 40px rgba(31, 41, 55, 0.16) !important;
}
@media (prefers-reduced-motion: reduce) {
  #insights-archive-articles .xpro-post-grid-item { transition: none !important; }
  #insights-archive-articles .xpro-post-grid-item:hover { transform: none !important; }
}
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-cat a,
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-category a {
  color: var(--hv-primary-deep) !important;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-title,
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-title a {
  color: var(--hv-foreground) !important;
  font-family: var(--hv-font-display);
  font-size: clamp(1.1rem, 1.6vw, 1.25rem);
  font-weight: 500;
  line-height: 1.3;
}
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-title a:hover {
  color: var(--hv-primary) !important;
}
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-content,
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-excerpt {
  color: var(--hv-reading-foreground) !important;
  font-size: 0.9375rem;
}
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-readmore a,
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-btn a {
  color: var(--hv-link) !important;
  font-size: 0.875rem;
  font-weight: 500;
  transition: color 0.2s ease;
}
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-readmore a:hover,
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-btn a:hover {
  color: var(--hv-link-hover) !important;
}
#insights-archive-articles .xpro-post-grid-item .xpro-post-grid-date {
  color: var(--hv-muted-foreground) !important;
  font-size: 0.8125rem;
}

/* Category archive: no-posts empty state — centred, muted */
#insights-archive-articles .xpro-post-no-found {
  text-align: center;
  color: var(--hv-muted-foreground);
  padding: 32px 0;
}

/* ─── Insights: prev/next navigation links ───────────────────────────────────
   Rendered by build_posts.py into the B' band (#hv-article-prevnext inner).
   Each link is a .hv-prevnext-link text-editor widget: ← Prev title / Next → .
   They sit in a flex row (space-between) so prev is left, next is right.
   On a single post (only one side), the flex layout naturally pushes the lone
   link to its edge via the empty-side placeholder .hv-prevnext-empty. */

.hv-prevnext-nav {
  width: 100%;
}
.hv-prevnext-nav .elementor-widget-text-editor {
  line-height: 1.5;
}
.hv-prevnext-link a {
  color: var(--hv-link);
  font-weight: 600;
  text-decoration: none;
  transition: color 0.2s ease;
  display: inline-block;
}
.hv-prevnext-link a:hover {
  color: var(--hv-link-hover);
}
/* Direction labels: small muted uppercase above the title */
.hv-prevnext-link .hv-prevnext-dir {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--hv-muted-foreground);
  margin-bottom: 4px;
}
.hv-prevnext-link .hv-prevnext-title {
  display: block;
  font-size: 0.9375rem;
}
/* Next link aligns right */
.hv-prevnext-next {
  text-align: right;
  margin-left: auto;
}

/* ─── Verteal-inspired sticky founder-quote col (wave 2, 2026-06-17) ──────────
   Two-column grid where the left column is position:sticky while the right
   scrolls freely. Purely CSS — no JS, no plugin. Reduced-motion safe (sticky
   is a layout feature, not an animation). Applied to About §1 (#about-story)
   which already carries the founder narrative left + orbital figure right.

   Contract: the outer section band must NOT have overflow:hidden (Elementor
   default is visible — keep it). The sticky column's top offset aligns with the
   pinned nav (--hv-header-offset = 96px, from the scroll-margin-top anchor rule).
   The sticky effect is dormant on mobile (stacked column, no scrolling neighbour).

   Author hook: add class `hv-sticky-col` to the left column widget container
   (via Elementor → Advanced → CSS Classes on the column's widget parent).
   Because _css_classes renders on WIDGETS but not containers (lesson 33), the
   sticky column is achieved by targeting the left .e-con by position within
   #about-story, not a container class. */

/* About story §1: left prose col sticky, right orbital scrolls.
   DOM: .e-con-inner > baca3d5 (left) + a28490d (right = #hv-orbital).
   Target the left .e-con directly as first child of .e-con-inner. */
@media (min-width: 768px) {
  #about-story > .e-con-boxed > .e-con-inner > .e-con:first-child {
    position: sticky;
    top: calc(var(--hv-header-offset, 96px) + 32px);
    align-self: start;
  }
}

/* ─── Verteal full-width card-close row (wave 2, 2026-06-17) ──────────────────
   Full-bleed card row that closes a section on one quiet next step. Uses the
   existing card system surface rules (CARD-SYSTEM block above). The .hv-card-close
   modifier produces full-bleed card strips with a single unified CTA accent.

   Note (2026-06-20): #solutions-capabilities was removed from the Solutions hub.
   This CSS block remains for future use on any page that needs the full-bleed
   card-close pattern; apply _element_id="hv-card-close" to the section.

   Visual contract: cards fill the full band width (no max-width cap on the row);
   each card has the brand shadow + white surface; a quiet left accent strip
   (3px, primary orange) separates from a pure CTA card (green band). */

/* Utility: suppress Elementor's default container padding on a row so cards
   reach true full-bleed within the band. Apply _element_id="hv-card-close" to
   the section to scope. */
#hv-card-close > .e-con-boxed > .e-con-inner > .e-con {
  gap: 0 !important;
}
#hv-card-close > .e-con-boxed > .e-con-inner > .e-con > .e-con {
  background: #ffffff;
  border: 0 !important;
  border-left: 3px solid var(--hv-primary) !important;
  border-radius: 0 !important;
  padding: 32px 40px !important;
  box-shadow: none !important;
}
/* First card: no left border (joins the section bg) */
#hv-card-close > .e-con-boxed > .e-con-inner > .e-con > .e-con:first-child {
  border-left: 0 !important;
}
/* On mobile: stack with a top hairline separator instead of left accent */
@media (max-width: 767px) {
  #hv-card-close > .e-con-boxed > .e-con-inner > .e-con > .e-con {
    border-left: 0 !important;
    border-top: 3px solid var(--hv-primary) !important;
  }
  #hv-card-close > .e-con-boxed > .e-con-inner > .e-con > .e-con:first-child {
    border-top: 0 !important;
  }
}

/* ─── Insights index: category chips (pill row) ──────────────────────────────
   The #insights-categories section carries two icon_list widgets (left/right
   column split). The recipe sets `layout:"inline"` per item but lesson 18
   confirms that alone does NOT produce a horizontal flow — the UL still renders
   as a vertical stacked list. This block converts both columns into a wrapping
   horizontal pill row using token-derived colours (no raw hex), scoped entirely
   to #insights-categories so nothing leaks to other icon-lists.

   Architecture:
   - The two inner columns (left/right) each hold one icon_list. We stack the
     two ULs into a SINGLE wrapping flex row by making their parent container
     also flex-wrap, and each UL itself flex+wrap. The row container already
     has `flex_direction:"row"` + `flex_direction_mobile:"column"` from the
     recipe — at ≤767px the two ULs stack column, and each UL still wraps its
     own chips. On desktop both ULs are in the same row (the recipe row) and
     each wraps independently within its column.
   - Each chip is an `.elementor-icon-list-item` `<li>`. On the link-face
     `<a>`, we apply pill styling: warm-sand fill (--hv-background-warm),
     calm border (--hv-border), full pill radius, comfortable padding, and
     an orange hover (--hv-primary fill with cream text).
   - The icon (fa-arrow-right) is hidden — an arrow in a pill chip is noise.
   - The kit `text_color` global on the widget is `hv_link`, so each `<a>`
     inherits the link-blue. We override to ink (--hv-foreground) on the pill
     face and flip to cream on hover so the orange badge stays legible.
   - `link_click:"inline"` was NOT set in the recipe (lesson 18: it sets per-
     item inline but doesn't affect the UL). We rely purely on CSS here.       */

/* Make each icon-list UL inside the category section flow as a wrapping flex row */
#insights-categories .elementor-icon-list-items {
  display: flex !important;
  flex-wrap: wrap !important;
  gap: 10px !important;
  /* Clear the browser-default list indent */
  padding: 0 !important;
  margin: 0 !important;
}

/* Each list item: inline-block reset so flex handles sizing */
#insights-categories .elementor-icon-list-item {
  display: inline-flex !important;
  margin: 0 !important;
  padding: 0 !important;
}

/* Pill face: the <a> tag wrapping each item.
   The <a> is bare (class="") in this Elementor build — a.elementor-icon-list-item-link
   never matches. Use the child combinator > a instead. */
#insights-categories .elementor-icon-list-item > a {
  display: inline-flex !important;
  align-items: center !important;
  gap: 0 !important;
  background: var(--hv-background-warm) !important;
  border: 1px solid var(--hv-border) !important;
  border-radius: 999px !important;
  padding: 8px 18px !important;
  color: var(--hv-foreground) !important;
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.4;
  text-decoration: none !important;
  white-space: nowrap;
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

/* Hover: orange fill, cream text — calm-over-loud */
#insights-categories .elementor-icon-list-item > a:hover {
  background: var(--hv-primary) !important;
  border-color: var(--hv-primary) !important;
  color: var(--hv-background) !important;
}

/* Hide the arrow icon inside each pill (redundant noise in a chip) */
#insights-categories .elementor-icon-list-icon {
  display: none !important;
}

@media (prefers-reduced-motion: reduce) {
  #insights-categories .elementor-icon-list-item > a {
    transition: none !important;
  }
}

/* Mobile: chips wrap naturally on small viewports (flex-wrap handles it);
   the two-column row stacks via recipe flex_direction_mobile:"column". */

/* ─── Insights single post: prose measure cap (~700px centred) ───────────────
   Problem: at 1920px the body inner is 1320px boxed; H1/prose/meta widgets
   span the full 1320px column → ~149 chars/line at 18px, far past the ~66–75
   char comfort zone. Fix: cap the prose widgets at 700px centred, leaving
   figure containers (which are .e-con, not widgets, and have their own
   boxed_width:760px in the recipe) completely unaffected.

   Widgets that get the cap (their CSS classes render on .elementor-element
   wrappers — lesson 33: _css_classes renders on WIDGETS):
     .hv-article-title    — H1 heading widget
     .hv-article-prose    — body text-editor widgets (body-1, body-2)
     .hv-article-sources  — sources text-editor widget (already max-width:760px
                             below; override to 700px to align with prose column)

   Widgets / elements that must stay at band width:
     [id^="hv-fig-"]      — figure containers (container, not widget → untouched)
     #hv-article-prevnext — separate band (own boxed 1320px inner)
     .hv-bridge-link      — back-to-insights in its own band
     #hv-article-image    — featured-image band (separate from body band)

   Meta band: the meta widget sits in #hv-article-meta (its own band with a
   separate 1320px inner). We cap it via the band-scoped selector below so it
   aligns to the same ~700px reading column as the body prose.

   Specificity: these rules add max-width on the .elementor-element wrapper
   (which is a flex item in a flex-column inner). !important is NOT needed —
   Elementor doesn't set max-width on widget wrappers by default. margin auto
   centres within the 1320px boxed column. text-align:left preserves the
   left-aligned prose within the centred column. */

.hv-article-title,
.hv-article-prose {
  max-width: 700px !important;
  width: 100% !important;
  margin-left: auto !important;
  margin-right: auto !important;
  text-align: left;
}

/* Sources block: override the existing 760px max with 700px to align
   with the prose column (the sources sit at the foot of the prose column). */
.hv-article-sources {
  max-width: 700px !important;
}

/* Meta band: cap the single meta widget inside #hv-article-meta to the
   same 700px column. The meta inner has no _element_id so we target via
   the band id + .elementor-widget (only one widget in that inner). */
#hv-article-meta .elementor-widget {
  max-width: 700px !important;
  width: 100% !important;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* Mobile (≤767px): column is narrower than 700px — disable the cap so the
   widget takes natural full width of the inner (no wasted side space). */
@media (max-width: 767px) {
  .hv-article-title,
  .hv-article-prose,
  .hv-article-sources,
  #hv-article-meta .elementor-widget {
    max-width: 100% !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
  }
}
