/**
 * Global mobile responsive overlay (adopted from Brad's source `data-qc-responsive-overlay v1`).
 * Enqueued site-wide, LAST, so it normalizes mobile behavior on ALL pages:
 * stacks grids/cards/flex rows, scales typography, normalizes padding, scrolls
 * tables, and guards overflow. This is the scalable fix for the 105-page set.
 */
/* =====================================================================
   QC Responsive Overlay v1.0  —  2026-04-23
   ---------------------------------------------------------------------
   Purpose: normalize mobile behavior across pages whose per-page <style>
   blocks use inconsistent breakpoint systems. Desktop layouts are NOT
   touched — rules fire only inside @media queries at ≤1024px and ≤640px.

   Delivery: injected as a style data-qc-responsive-overlay block immediately
   before the closing head tag on every page, so it wins the cascade against
   earlier style blocks. !important is used ONLY where per-page styles use IDs,
   inline styles, or high-specificity descendant selectors.

   Portability: when launching on WordPress, paste the contents of this
   file into Elementor → Site Settings → Custom CSS. It will apply to
   every page whose HTML widgets contain the markup patterns below.

   Breakpoints:
     Mobile  ≤  640px
     Tablet  ≤ 1024px
     Desktop ≥ 1025px  (no overlay rules — page-level CSS owns desktop)
   ===================================================================== */


/* =====================================================================
   Universal overflow safety — every viewport
   ===================================================================== */
/* overflow-x MUST be clip, not hidden. "hidden" on html+body makes body its own
   scroll container, which silently kills position:sticky on the global nav (the
   header stops sticking sitewide). "clip" suppresses horizontal scroll without
   creating a scroll container, so the sticky header keeps working. */
html,
body {
  overflow-x: clip !important;
  max-width: 100% !important;
}

img,
video,
iframe {
  max-width: 100% !important;
  height: auto;
}

/* Product images on the Technology sensor pages were stretched about 67% wide.
   Reported by the client on /technology/lora-sensors/, 16 Sept.

   Brad's page CSS caps these images with BOTH max-width:70% and max-height:96%.
   CSS keeps an image's aspect ratio when both limits bite only while width and
   height are both auto. His source <img> tags carry no width or height, so on
   his pages they were auto and the pictures looked right. The import pipeline
   adds width and height attributes to every image so the browser can reserve
   space and avoid layout shift, and a width attribute is a fixed width. Then
   max-width clamps the width, max-height clamps the height on its own, and the
   square product shot is drawn into a 5:4-ish box.

   width:auto puts his original behaviour back. It does not cost the layout-shift
   protection: the browser still takes the aspect ratio from the attributes to
   reserve the box. Any author rule outranks an HTML width attribute, so this
   wins regardless of where the page's own <style> block sits. */
.sensor-imgwrap img {
  width: auto;
}

/* Same fault across the plain-HTML documents (the 19 Digital Probes sheets and
   the Nova SPY User Guide, i.e. every document not built on <doc-page>).

   None of their 89 source <img> tags carries a width attribute, so every width
   attribute on them now came from the import. Where the source sizes an image
   by height (the Nova guide's inline max-heights, its 36px symbol icons, its
   44px cover logo) that attribute wins and the picture is drawn at the wrong
   size or shape: the client's own file draws all of them correctly.

   :where() gives this rule zero specificity on purpose. It must beat only the
   width ATTRIBUTE, which any author rule does, and lose to every width the
   source CSS itself sets, so the source keeps full control of its own layout. */
:where(.qc-document:not(:has(doc-page))) img {
  width: auto;
}


/* =====================================================================
   Tablet & below: ≤1024px
   Container normalization + scrollable tables
   ===================================================================== */
@media (max-width: 1024px) {

  main,
  section,
  article,
  header,
  footer,
  .container,
  .wrapper,
  [class*="container"],
  [class*="wrapper"] {
    max-width: 100% !important;
    box-sizing: border-box !important;
  }

  /* Data tables scroll horizontally rather than breaking the layout.
     Excluded on <doc-page> documents: those sheets are scaled to fit as a
     whole, so turning their spec tables into scrollers breaks the page. */
  body:not(:has(doc-page)) table {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
  }
}


/* =====================================================================
   Mobile: ≤640px
   Typography scale, padding, layout stacking, touch targets
   ===================================================================== */
@media (max-width: 640px) {

  /* ---- Fluid, hierarchical typography ---- */
  h1 {
    font-size: clamp(28px, 7.5vw, 40px) !important;
    line-height: 1.15 !important;
    overflow-wrap: break-word;
  }

  h2 {
    font-size: clamp(22px, 5.5vw, 30px) !important;
    line-height: 1.2 !important;
    overflow-wrap: break-word;
  }

  h3 {
    font-size: clamp(18px, 4.5vw, 24px) !important;
    line-height: 1.25 !important;
  }

  h4 {
    font-size: clamp(16px, 4vw, 20px) !important;
    line-height: 1.3 !important;
  }

  p,
  li {
    font-size: clamp(15px, 4vw, 17px);
    line-height: 1.55;
  }

  /* Note: previously had `word-break: break-word` here, which some
     browsers treat as break-all and orphan single letters at line
     edges (e.g. a lone "S" hanging off a heading). overflow-wrap:
     break-word (applied above on h1/h2) is the safe modern version
     and only breaks at word boundaries when the line would overflow. */

  /* ---- Consistent side padding across structural elements ----
     CLIENT ISSUE #19, 19 Aug 2026. This list used to include main, article,
     header, footer and nav. Those are the FULL-BLEED wrappers: #qc-main and
     article.qc-page wrap the entire page, and most sections paint their own
     background. Padding the wrapper pushes every section 16px inboard and
     leaves the dark <body> showing as a frame down both edges of every
     light section. That is the "all pages are padded from the side" the
     tester photographed, and why he read it as an Elementor container
     default. There is no Elementor here.

     The gutter now sits only on the elements that are meant to carry it:
     the section (which owns the background, so its padding is inside the
     colour) and the inner containers. That reproduces the client's own
     files, which give section 16px and .container 16px at 440px. */
  section,
  .container,
  .wrapper,
  [class*="container"],
  [class*="wrapper"] {
    padding-left: 16px;
    padding-right: 16px;
  }

  main,
  section,
  article,
  header,
  footer,
  nav,
  .container,
  .wrapper,
  [class*="container"],
  [class*="wrapper"] {
    max-width: 100% !important;
    box-sizing: border-box !important;
  }

  /* ---- Stack flex rows and CSS grids on mobile ----
     Override desktop multi-column layouts. Intentional mobile-grid
     designs can opt out with `.keep-grid` on the container.
     Expanded in v3 to cover "cards / items / tiles" suffixes used
     across hub pages (.regulatory-cards, .feature-items, etc.) and
     to catch inline-styled grids that bypass CSS class selectors. */
  .row,
  .columns,
  [class*="row"]:not(.keep-grid),
  [class*="columns"]:not(.keep-grid),
  [class*="grid"]:not(.keep-grid),
  [class*="flex"]:not(.keep-grid),
  [class*="cards"]:not(.keep-grid),
  [class*="items"]:not(.keep-grid),
  [class*="tiles"]:not(.keep-grid) {
    flex-direction: column !important;
    grid-template-columns: 1fr !important;
    gap: 16px !important;
  }
  /* Note: previously included [style*="grid-template-columns"] which broke
     the Pricing page's 5-tier sticky table header by flattening its
     intentional inline grid to 1fr. Pages that need an inline grid to
     stack on mobile now use the .keep-grid escape hatch inverted — add
     a class like `grid-three` or stack via surrounding container. */

  /* ---- Touch target minimums for interactive elements ---- */
  a.btn,
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  [role="button"],
  .button,
  [class*="button"] {
    min-height: 44px;
  }

  /* ---- Form inputs stay inside their container + prevent iOS zoom ---- */
  input,
  select,
  textarea {
    max-width: 100% !important;
    box-sizing: border-box !important;
    font-size: 16px !important;
  }
}


/* =====================================================================
   Tech spec / user guide template (Resources hub)
   ---------------------------------------------------------------------
   All 33+ Resources documents share .qc-header and .title-band markup.
   At desktop: .title-band is a two-column flex with .title-left (flex:1)
   + .title-right (flex:0 0 220px, product image). At mobile the fixed
   220px right column squeezed .title-left to a sliver — the "text
   shifted to one side, other side blank" pattern Brad flagged.

   Fix: stack the two columns, center the product image, reduce padding
   so the narrow viewport gets more breathing room, and let .img-row
   (3-up product thumbnail strips) wrap instead of staying side-by-side.
   ===================================================================== */
@media (max-width: 640px) {
  .qc-header {
    padding: 12px 16px !important;
    gap: 12px !important;
    flex-wrap: wrap !important;
  }
  .qc-header__right {
    margin-left: 0 !important;
    text-align: left !important;
    width: 100% !important;
  }
  .qc-header img {
    height: 32px !important;
  }

  .title-band,
  .title-area {
    flex-direction: column !important;
    padding: 24px 16px !important;
    gap: 20px !important;
    align-items: stretch !important;
  }
  .title-left,
  .title-right {
    flex: 1 1 100% !important;
    width: 100% !important;
    max-width: 100% !important;
    text-align: left !important;
  }
  .title-left h1 {
    font-size: clamp(24px, 6.5vw, 32px) !important;
    line-height: 1.2 !important;
    margin-bottom: 10px !important;
  }
  .title-right {
    text-align: center !important;
  }
  .title-right img {
    max-width: 140px !important;
    max-height: 140px !important;
    margin: 0 auto !important;
  }

  /* 3-up product image rows wrap gracefully instead of hitting the edge.
     Explicit flex-direction: row overrides the universal stacker above
     (`.img-row` would otherwise match `[class*="row"]` and flip to column,
     which combined with any inline `align-items: flex-end` on the markup
     would right-stack the images — flagged on S-15 LoRa Range Extender). */
  .img-row,
  .img-grid {
    flex-direction: row !important;
    flex-wrap: wrap !important;
    gap: 12px !important;
    align-items: center !important;
    justify-content: center !important;
  }
  .img-grid-item {
    flex: 1 1 calc(50% - 12px) !important;
    max-width: calc(50% - 12px) !important;
  }

  /* Data-dense section rows inside tech spec tables need a cleaner break */
  .section-row td {
    padding: 8px 10px !important;
  }
}


/* =====================================================================
   Pill-TOC (sub-nav on Solutions/REM hub pages)
   ---------------------------------------------------------------------
   `.pill-toc` is a `<nav>` element that's sticky at the top of Solutions
   and REM hub pages, holding 5-8 anchor pills ("Regulatory Overview,"
   "Monitoring Setup," etc.). On desktop it renders as a single horizontal
   row; on mobile Brad's preference is to wrap the pills and center them
   so every option stays visible rather than hidden behind a horizontal
   scroll. Trade-off: tall pill-tocs take 2-4 lines of vertical space on
   phones, but no option gets hidden.
   ===================================================================== */
@media (max-width: 640px) {
  .pill-toc,
  nav.pill-toc,
  .pill-toc .pill-container,
  .pill-toc .pill-toc-inner {
    padding: 10px 12px !important;
    gap: 6px !important;
    flex-wrap: wrap !important;
    justify-content: center !important;
    overflow-x: visible !important;
    white-space: normal !important;
    flex-direction: row !important;
    min-width: 0 !important;  /* override `min-width: max-content` on S-56's .pill-container that would otherwise block wrapping */
    max-width: 100% !important;
  }
  .pill-toc > *,
  .pill-toc .pill,
  .pill-toc .pill-toc-item,
  .pill-toc .pill-link {
    flex-shrink: 0 !important;
    white-space: nowrap !important;
  }
}


/* =====================================================================
   Landing hero chrome (B-10 Risk Assessment, B-11 ROI Calculator)
   ---------------------------------------------------------------------
   `.landing-eyebrow` — the orange-dot pill above the H1.
   `.landing-meta` — the "~6 minutes · 25 questions · Immediate results"
   trio with pipe separators between spans.

   Fixes applied at mobile only:
   - Force the orange dot to the LEFT of the pill text (align-self: flex-
     start) so it never appears vertically centered or ambiguously placed
     when the pill text wraps to two lines.
   - Stack the landing-meta spans vertically, hiding the pipe separators
     so "Immediate results" sits on its own centered line rather than
     getting divided by a lone `|`.
   ===================================================================== */
@media (max-width: 640px) {
  /* Keep the pill on a single horizontal line with the dot aligned to the
     text's vertical center. Earlier attempt used flex-start + margin-top,
     which visibly orphaned the dot above the text baseline — reverting to
     centered alignment which was the original (correct) design intent. */
  .landing-eyebrow {
    align-items: center !important;
    flex-wrap: nowrap !important;
    max-width: 100% !important;
  }
  .landing-eyebrow::before {
    flex: 0 0 6px !important;
    align-self: center !important;
    margin-top: 0 !important;
  }

  .landing-meta {
    flex-direction: column !important;
    align-items: center !important;
    gap: 6px !important;
  }
  .landing-meta span {
    display: block !important;
    gap: 0 !important;
  }
  /* Hide the pipe separator — the pseudo-element between consecutive
     spans that renders the `|` bar */
  .landing-meta span + span::before {
    display: none !important;
  }
}


/* =====================================================================
   Client Portal navigation (C-02)
   ---------------------------------------------------------------------
   .portal-nav .container is a 3-column grid (1fr auto 1fr) with a
   fixed 120px height. At mobile the three zones (logo-wrap | nav-links
   | nav-back) squeeze together and the "Main Site" back link overlaps
   the "MySirius Portal / Client Access" text inside the logo-wrap.

   Mobile fix: collapse to a 2-column layout (logo-group | main-site),
   hide the secondary "Client Access" subtitle, and tighten typography
   so the logo + portal label + back link all fit cleanly.
   ===================================================================== */
@media (max-width: 640px) {
  .portal-nav .container {
    grid-template-columns: auto 1fr !important;
    height: auto !important;
    padding: 12px 14px !important;
    gap: 12px !important;
    align-items: center !important;
  }
  .portal-nav .logo-wrap {
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    gap: 6px !important;
  }
  .portal-nav .logo-wrap .logo-img {
    max-height: 32px !important;
    width: auto !important;
  }
  .portal-nav .logo-divider {
    height: 20px !important;
    margin: 0 4px !important;
  }
  .portal-nav .logo-portal-tag {
    font-size: 10.5px !important;
    line-height: 1.2 !important;
  }
  /* Hide the redundant "Client Access" subtitle — user is already in the
     portal, the primary "MySirius Portal" label is enough */
  .portal-nav .logo-portal-tag > span:last-child {
    display: none !important;
  }
  /* Main Site link anchored to the far right */
  .portal-nav .nav-back {
    justify-self: end !important;
    font-size: 11px !important;
    white-space: nowrap !important;
  }
  .portal-nav .nav-back svg {
    width: 12px !important;
    height: 12px !important;
  }
}


/* =====================================================================
   Logo sizing (B-10 Risk Assessment, B-11 ROI Calculator)
   ---------------------------------------------------------------------
   The .landing-logo img was set to 44px on both pages, which rendered
   visually undersized in the hero at every viewport. Bumping to 72px.
   Applied site-wide via the overlay so any other page adopting the
   same .landing-logo pattern picks up the corrected size.
   ===================================================================== */
.landing-logo img {
  height: 72px !important;
  width: auto !important;
}
@media (max-width: 640px) {
  .landing-logo img {
    height: 56px !important;
  }
}


/* =====================================================================
   REM + Solutions landing-page safety nets (Wave C)
   ---------------------------------------------------------------------
   Hub pages under /REM/ and /Solutions/ share a family of classes:
   .solution-hero (full-viewport), .solution-visualization (400px-tall
   illustration panel), .flow-node (inline label nodes), .globe-panel
   (map panel), etc. Most already have page-level @media rules at
   768px; these fire inside the 375-639 range where page-level rules
   don't reach.
   ===================================================================== */
@media (max-width: 640px) {
  /* Illustration panels beside heros shouldn't eat 400px of vertical
     real estate when stacked below hero copy on a phone */
  .solution-visualization {
    height: 220px !important;
    min-height: 0 !important;
  }

  /* Defensive safety net — page CSS usually sets min-height: auto at
     768px, but catch any page that didn't */
  .solution-hero,
  .about-hero {
    min-height: auto !important;
  }

  /* Flow diagrams — let nodes shrink tight so 4+ fit in a wrapped row */
  .flow-node {
    min-width: 80px !important;
    padding: 10px 12px !important;
  }

  /* Wine/Spirits hero + Multi-site globe panel — soften tall viewport
     anchors that waste screen on short phones */
  .globe-panel {
    min-height: 260px !important;
  }
}


/* =====================================================================
   Page-specific fixes (Wave C — scoped selectors only)
   ---------------------------------------------------------------------
   Each block below only fires on the single page whose selectors
   match. Keeping them in the overlay for one source-of-truth; during
   Elementor port, each block can be copied into the matching page's
   HTML widget wrapper instead of global CSS.
   ===================================================================== */

/* ---- C-01 About page ----
   Original CSS has a typo: @media(max-width:1024px) targets '.hero .container'
   but the actual element is '.about-hero > .container'. Mobile grid never
   stacks because of the miss. Also the 5-column .about-lifecycle-track
   has no mobile rule at all. */
@media (max-width: 1024px) {
  .about-hero > .container {
    grid-template-columns: 1fr !important;
    gap: 40px !important;
  }
}
@media (max-width: 640px) {
  .about-hero {
    padding: 48px 0 56px !important;
  }
  .about-lifecycle-track {
    grid-template-columns: 1fr !important;
    gap: 12px !important;
  }
  /* Decorative big-number elements scale down gracefully on mobile */
  .auth-pillar-num,
  .callout-value {
    font-size: clamp(24px, 6vw, 42px) !important;
  }
}

/* ---- C-03 Homepage ----
   .s7-right panel is hard-coded min-height: 520px with no mobile rule —
   produces a tall empty panel when content is short on a 375 viewport. */
@media (max-width: 640px) {
  .s7-right {
    min-height: auto !important;
    padding: 32px 20px !important;
  }
}

/* -----------------------------------------------------------------
   Homepage hero dashboard mockup (.hero-dashboard): its .dash-kpis is a
   3-col grid that gets cramped at phone widths, splitting the big odometer
   numbers across lines. Stack the KPIs and keep each value on one line.
   ----------------------------------------------------------------- */
@media (max-width: 640px) {
  .hero-dashboard .dash-kpis { grid-template-columns: 1fr !important; gap: 10px !important; }
  .hero-dashboard .kpi-value { white-space: nowrap !important; display: flex !important; flex-direction: row !important; align-items: baseline !important; }
  .hero-dashboard .kpi-value .odo { white-space: nowrap !important; }
}

/* -----------------------------------------------------------------
   Hard-clip horizontal marquee/ticker tracks so they can never pan on real
   devices.

   The block that used to sit here zeroed the padding on every inner
   [class*="container"] to cancel the 16px this file was adding to <main>.
   <main> is no longer padded (see issue #19 above), so the client's own
   container padding is left alone and the inset matches his files.
   ----------------------------------------------------------------- */
@media (max-width: 640px) {
  [class*="marquee"], [class*="ticker"], [class*="logos"], [class*="-track"] { overflow-x: clip !important; max-width: 100% !important; }
  .timeline-track { overflow-y: visible !important; }
}

/* -----------------------------------------------------------------
   CLIENT ISSUE #20, 19 Aug 2026: "the Resources page opens a menu on scroll
   that cannot be minimised."

   It is not a menu. It is the Resources hub filter bar, .subnav, which the
   client designs as position:sticky with two WRAPPING pill rows. Our blanket
   mobile stacking selector above matches [class*="row"], so .subnav-row was
   forced to flex-direction: column. The bar grew from the 249px it measures in
   the client's own file at 440px to 794px, and because it is sticky it then
   occupies 83% of the phone screen for the whole scroll and cannot be
   dismissed. Wrapping rows must stay rows.
   ----------------------------------------------------------------- */
@media (max-width: 640px) {
  .subnav-row,
  [class*="pill-row"],
  [class*="chip-row"],
  [class*="filter-row"],
  [class*="tab-row"],
  [class*="-pills"],
  [class*="-chips"] {
    flex-direction: row !important;
    flex-wrap: wrap !important;
    gap: 8px 10px !important;
  }
}

/* -----------------------------------------------------------------
   Page wrappers stay full-bleed. #qc-main and article.qc-page carry no side
   padding now (issue #19), so sections keep their own backgrounds edge to
   edge. This rule also catches assembled pages that baked a padding value
   onto the wrapper itself.
   ----------------------------------------------------------------- */
@media (max-width: 640px) {
  #qc-main, #qc-main > article, article.qc-page, .qc-page { padding-left: 0 !important; padding-right: 0 !important; }
}

/* ===========================================================================
   Case studies: the responsive layer the source never shipped.

   The five 11_Case_Studies pages contain ZERO @media rules. Every other section
   of the same delivery has them (06_Solutions_REM 447, 08_Buyers_Guide 182,
   03_Core 175), so this is a gap in those five files rather than a house style.
   Their desktop layout therefore applies verbatim at 390px: 29 elements at
   width:1200px, 17 at 660px, and twenty grids at 96px 1fr 168px, which leaves
   almost no room for the middle column and renders headings one letter per line.

   Measured on /case-studies/versiti/ at 390px before this rule: 43 elements
   overflowing, worst 544px inside a 390px box. The page reports no document
   overflow because the outermost container is overflow:hidden, so the content is
   simply cut off and unreachable rather than scrollable.

   The markup carries no classes at all, only inline styles, so these have to be
   attribute selectors and they have to be !important to beat an inline style.
   Everything is scoped to .qc-case-study, the wrapper our importer adds, so it
   cannot reach any other page.

   This is a stopgap. If the client ships responsive source for these five, drop
   this block and re-import.
   =========================================================================== */
@media (max-width: 900px) {
  /* Multi-column grids collapse to one column. */
  .qc-case-study [style*="grid-template-columns"] {
    /* minmax(0, 1fr), not 1fr. A bare 1fr is minmax(auto, 1fr), and the auto
       minimum is the item's min-content width, so one wide child kept the column
       at 595px inside a 342px box on the hub. */
    grid-template-columns: minmax(0, 1fr) !important;
  }
  /* Nothing may be wider than its parent, whatever the inline width says. */
  .qc-case-study [style*="width"] {
    max-width: 100% !important;
  }
  /* Grid and flex children default to min-width:auto, which refuses to shrink
     below their content and is what forces the one-letter-per-line wrapping. */
  .qc-case-study * {
    min-width: 0 !important;
  }
  /* Desktop gaps are far too large once everything is stacked. */
  .qc-case-study [style*="gap"] {
    gap: 24px !important;
  }
  /* The attribute selectors above only reach elements that name a width. The
     remaining overflow came from children with no inline width of their own
     sitting inside a padded parent, so nothing is allowed to exceed its box. */
  .qc-case-study * {
    max-width: 100% !important;
    box-sizing: border-box;
  }
  /* Rows of buttons and breadcrumbs were laid out to fit 1200px and cannot fit
     390px on one line. */
  .qc-case-study [style*="display: flex"],
  .qc-case-study [style*="display:flex"],
  .qc-case-study [style*="inline-flex"] {
    flex-wrap: wrap !important;
  }
  /* Long part numbers and URLs in the sidebar would otherwise push the column. */
  .qc-case-study { overflow-wrap: anywhere; }

  /* Wide fixed-size media and tables scroll inside their own box rather than
     being clipped by an ancestor. */
  .qc-case-study table {
    display: block;
    overflow-x: auto;
    max-width: 100%;
  }
  /* The outermost sections clip their overflow, which is what hid all of this
     from a document-level overflow check. */
  .qc-case-study section,
  .qc-case-study > div {
    overflow-x: clip;
  }
}

@media (max-width: 640px) {
  /* Vertical rhythm built for a 1200px canvas is enormous on a phone. */
  .qc-case-study section[style*="padding"] {
    padding-top: 48px !important;
    padding-bottom: 48px !important;
  }
}

/* ---------------------------------------------------------------------------
   Case studies: reserve the height the runtime is about to fill.

   support.js hides the <x-dc> template on load and React re-renders it a beat
   later. Until then the wrapper is 64px tall and the footer sits inside the
   viewport, so when the content arrives the footer is displaced by 3064px.
   Measured CLS 0.926 on /case-studies/ and /case-studies/versiti/ in the 29 Aug
   performance pass.

   A layout shift only counts against CLS when it moves something the visitor can
   actually see, so reserving a viewport of height keeps the footer below the fold
   until the real content replaces the reservation.

   Two alternatives were tried and measured first, and both were worse:
     - Prerendering a snapshot into the stored HTML, as /buyers-guide/pricing/
       does. That page ships an EMPTY div#root; these ship a full template, so the
       snapshot duplicated every page and gave each two <h1>. CLS 0.002, but not
       at that price.
     - Unhiding <x-dc> so the raw template paints immediately. React then replaces
       it with differently-sized markup: CLS went UP, to 1.85 and 2.09.
   --------------------------------------------------------------------------- */
.qc-case-study { min-height: 100vh; }

/* ---------------------------------------------------------------------------
   Same reservation for the other two runtime-rendered families.

   The 54 library documents use the identical .dc runtime as the case studies:
   an <x-dc> template that support.js hides and React re-renders, so the page is
   empty until then and the footer is displaced when the content lands. Measured
   CLS 0.575 on the LoRa T1 spec sheet.

   /buyers-guide/pricing/ is a different build but the same shape. It carries a
   server-rendered snapshot, but mounts with createRoot rather than hydrateRoot,
   so React discards the snapshot and re-renders. Measured CLS 0.176.

   Reserving a viewport of height keeps the footer below the fold in both cases,
   so the replacement no longer moves anything the visitor can see. Same fix as
   .qc-case-study above, and the same reasoning for why the alternatives were
   rejected.
   --------------------------------------------------------------------------- */
.qc-document__body { min-height: 100vh; }
#qc-main #root { min-height: 100vh; }
