/*
  WeLaunch UI page-level stylesheet.
  The component library ships its own CSS (@welaunch/ui/styles.css, which already includes the
  token sheet). Every React module imports ../../lib/library.ts, so the library CSS is bundled by
  the CMS React pipeline and emitted through standard_header_includes; this file only carries
  page-chrome rules that live outside any module.
  One CSS custom property per line: HubSpot's CSS pipeline drops the 2nd+ declaration on a shared line.
*/
/* THE CANVAS FOLLOWS THE PAGE: hero colour at the top, footer colour at the bottom. iOS Safari
   paints its toolbars and the overscroll (rubber-band) regions from the BODY background (html is
   ignored for this), samples the toolbar tint at first paint, and tracks live changes for the
   overscroll. Chrome never extends the canvas, so none of this shows there. Because the hero and
   the footer are different Launch Indigo steps, one static canvas colour mismatches one end (Conner's phone,
   2026-09-19: deep canvas matched the footer and left a dark strip above the bright hero). The
   documented answer is a scroll-driven animation on the body colour: it holds the hero's Launch Indigo
   until 92% of the scroll and turns to the footer's deep Launch Indigo at the end, so the top overscroll
   and the first-paint tint see the hero and the bottom sees the footer. `.wl-page` paints the real
   page ground on top, so the body colour is only ever visible beyond the document's edges. Static
   fallback for browsers without scroll timelines: the footer colour (the bottom strip is the one
   that reads as a cutoff; the top is behind the status bar). */
html {
  background: var(--launch-indigo-800, #0B27C5);
  color: var(--text-default, #292929);
  font-family: var(--font-body, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0;
  background-color: var(--surface-deep, #0C185A);
}
@supports (animation-timeline: scroll()) {
  body {
    background-color: var(--launch-indigo-800, #0B27C5);
    animation: wl-canvas linear both;
    animation-timeline: scroll(root);
  }
  @keyframes wl-canvas {
    0%, 92% { background-color: var(--launch-indigo-800, #0B27C5); }
    100% { background-color: var(--surface-deep, #0C185A); }
  }
}
.wl-page {
  background: var(--surface-page, #F9F9F9);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.wl-page__main {
  flex: 1 0 auto;
}

/* ---- the nav is allowed to stick ---------------------------------------------------------------
   `SiteHeader --sticky` is `position: sticky; top: 16px` and works everywhere the library is used.
   On a real HubSpot page it did not: measured over CDP on the Contact draft at 390 and at 1440,
   `getBoundingClientRect().top` of `.wl-site-header` read **-790 after an 800px scroll**, i.e. the
   pill scrolled away with the page (Conner: "should the nav move with the scroll? Right now it
   looks awkward").

   THE CAUSE, and it is neither of the two usual ones. No ancestor sets `overflow`, a `transform`,
   a `filter` or `contain`; every one of them reported `overflow: visible` in the probe. A sticky
   element is confined to its PARENT'S box, and the layout's module tag wraps the nav in
   (no HubL braces in this comment: the raw-asset scanner fails the build on them, see the README)
   `div#hs_cos_wrapper_wl_site_nav`, which the React island wraps again in `div[id^="island-"]`.
   Both wrappers are exactly the header's own height (70px and 60px in the probe), so the header had
   zero travel inside them and stuck for 0px before leaving with its parent. Nothing in the theme's
   CSS caused it; the wrappers are HubSpot's markup, which is why the fix is here and not in the
   library.

   `display: contents` removes both wrapper boxes from the layout tree without removing the
   elements, so the header becomes a direct child of `.wl-page` (whose box is the whole document)
   and sticks against it. The island still hydrates: the element is still in the DOM and is still
   React's root container, only its box is gone.

   Verified over CDP after the change: `top` = 16 at 800px scrolled, at 390 and at 1440.
   ---------------------------------------------------------------------------------------------- */
#hs_cos_wrapper_wl_site_nav,
#hs_cos_wrapper_wl_site_nav > div[id^="island-"],
#hs_cos_wrapper_wl_lp_nav,
#hs_cos_wrapper_wl_lp_nav > div[id^="island-"],
/* 2026-09-20: the nav is a global partial now, and HubSpot wraps a global partial in a third
   box, `div[data-global-resource-path="…/partials/header.hubl.html"]`, for the editor to find
   it. Same height as the header, same zero travel, same fix. Matched on the file name so the
   theme's own path prefix (which differs between portals) does not matter. */
div[data-global-resource-path$="/partials/header.hubl.html"] {
  display: contents;
}

/* ---- the page ground stays visible ------------------------------------------------------------
   `Section inset` paints a rounded panel and holds it inside a 20px margin, so the depth comes from
   the page ground showing around and behind it. That only works if nothing between <main> and the
   section paints a ground or adds a gutter of its own.

   Two things could: HubSpot's drag-and-drop wrappers, and the boilerplate `.dnd-section { padding:
   80px 20px }` rule that most themes ship. This theme ships no such rule, but the wrappers are
   HubSpot's, not ours, so they are pinned explicitly rather than left to a default that is outside
   this repo. A section's own `padding` prop is the only source of vertical rhythm on a page.
   ---------------------------------------------------------------------------------------------- */
.wl-page__main .dnd-section {
  padding: 0;
  background: none;
}
.wl-page__main .dnd-section > .row-fluid {
  max-width: none;
}
.wl-page__main .dnd-column,
.wl-page__main .dnd-module {
  padding: 0;
}

/* ---- the first band reaches the top of the phone (2026-09-19) ---------------------------------
   Conner, from an iPhone: a strip of page ground sat above the hero, with the nav pill floating in
   it. Two separate causes, both handled here.

   1. THE BAND. `@welaunch/ui` gives the first band on a page `edge="top"`: on a phone it drops its
      top and side insets and rounds its bottom corners only. That is a prop, and the theme cannot
      set it. The hero on `home` and `service` is a template-level module, and on `page` (About,
      Contact, Services, Portal audit) it is a dnd module whose field values live in the page
      payload, which this change is not allowed to touch. So the theme says the same thing
      STRUCTURALLY, by setting the two custom properties the library's own `--edge-top` rule sets.
      `Section.css` and `HeroMeshSplit.css` publish `--wl-section-radius` / `--wl-hero-radius` for
      exactly this: one contract, two ways to reach it, no duplicated geometry.

      `--wl-edge-top-flat` JOINED THE PAIR ON 2026-09-19, and it is the reason that sentence is now
      load-bearing rather than tidy. The library's own `--edge-top` rule also blends the band's
      first 160px up to the flat `--surface-dark` this file paints on `html`, so the band and the
      canvas behind the status bar meet with no line. That blend was scoped to the CLASS, which is
      the one thing this path cannot produce, so every first band on the live site had the right
      shape and the wrong top edge: the Home draft measured #2741CC on its top row against a canvas
      of #0B27C5. Setting the distance here is the whole fix, and a treatment on the band fades out
      over the same variable rather than over a second copy of the number.

      "FIRST" IS THE WHOLE SELECTOR, AND IT IS WORTH READING SLOWLY.
        `:not(:where(.wl-page__main :not(:first-child) *))`
      means "no element between <main> and this band has a preceding sibling". That is the only
      honest way to say FIRST here, because the depth is not fixed: on `home` and `service` the hero
      is a template module two levels under <main> (`#hs_cos_wrapper_wl_hero > section`), and on
      `page` it is a dnd module eight levels down, through
      `.container-fluid > .row-fluid-wrapper > .row-fluid > .span12 > .row-fluid-wrapper.row-number-N
      > .row-fluid > .span12 > #hs_cos_wrapper_*`. No `.dnd-section` anywhere: the dnd areas here
      render as the classic `container-fluid` grid, and every band on the page shares the first four
      ancestors, so a `> :first-child` descendant match catches ALL of them. The first draft of this
      rule did exactly that and bled the audit band on Services and the form panel on Portal audit to
      the screen edges. Only `row-number-1` has no preceding sibling, which is what the `:where()`
      test reads, at whatever depth it sits.

      `:where()` so the negation costs no specificity. And NO `:first-child` on the band itself,
      which the second draft had and which matched nothing at all: HubSpot injects a
      `<link rel="stylesheet">` for the module's own chunk INSIDE the module wrapper, ahead of the
      section, so a band is never the first child of its wrapper. The ancestor test is what carries
      the meaning anyway; the band's own position inside its wrapper says nothing about the page.

      These rules are UNLAYERED, so they beat the library's `@layer components` whatever their
      specificity, the same way every other override in this file does.

   2. THE NAV'S OWN BOX. The pill is `position: sticky; top: 16px` and stays that way -- but a
      sticky element still occupies its place in the flow, and that place was the 70px strip. The
      header box collapses to zero height so the hero starts at y=0 and the pill paints over it;
      the pill is `.wl-site-header__inner`, which keeps its own 10px offset and overflows the
      collapsed header. Sticky is unaffected: the header's containing block is `.wl-page`, whose
      box is the whole document (that is what the `display: contents` rule above bought), so it
      still has the full page to travel and still pins at 16px.

      Gated on the page actually starting with a band, by the same first-child test, so a blog
      listing, a system page or the module test rig (whose first wash hero is in row 3, under a
      rich-text intro) keeps the strip it is supposed to have. A nav with no box over type on the
      page ground would land on top of it.
   ---------------------------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .wl-page__main .wl-hero-mesh--wash:not(:where(.wl-page__main :not(:first-child) *)) {
    --wl-hero-inset: 0px;
    --wl-hero-radius: 0 0 var(--radius-card-lg) var(--radius-card-lg);
    --wl-edge-top-flat: 160px;
  }
  .wl-page__main .wl-section--inset:not(:where(.wl-page__main :not(:first-child) *)) {
    --wl-section-inset: 0px;
    --wl-section-radius: 0 0 var(--radius-card-lg) var(--radius-card-lg);
    --wl-edge-top-flat: 160px;
  }
  .wl-page:has(.wl-page__main .wl-hero-mesh--wash:not(:where(.wl-page__main :not(:first-child) *))) .wl-site-header--pill,
  .wl-page:has(.wl-page__main .wl-section--inset:not(:where(.wl-page__main :not(:first-child) *))) .wl-site-header--pill {
    height: 0;
    overflow: visible;
  }
}

/* ---- the hero slot ----------------------------------------------------------------------------
   The tiles inside the `Slot` under the hero. This is the one piece of composition the theme does
   itself: `Slot` is a container primitive with no opinion about its contents, and a grid of four
   icon-chip tiles is a page-level arrangement rather than a library section. It lives here rather
   than in a module's own stylesheet because a React module's CSS is bundled per module and this is
   chrome around a library primitive.

   The classes are prefixed `wl-hero-slot__` and appear nowhere in the library (field note 10: a
   class collision inside one composition is as damaging as one across two).
   ---------------------------------------------------------------------------------------------- */
.wl-hero-slot__tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}
.wl-hero-slot__tile {
  display: block;
  padding: 20px 18px 22px;
  border-radius: var(--radius-lg, 20px);
  color: inherit;
  text-decoration: none;
  transition: background var(--motion-duration-fast, 140ms) var(--motion-ease-standard, ease);
}
.wl-hero-slot__tile:hover,
.wl-hero-slot__tile:focus-visible {
  background: var(--surface-page, #F9F9F9);
}
.wl-hero-slot__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full, 999px);
  background: var(--surface-page, #F9F9F9);
  color: var(--brand-primary, #0066FF);
  margin-bottom: 14px;
}
.wl-hero-slot__title {
  font-family: var(--font-heading, "Plus Jakarta Sans", sans-serif);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.3;
  margin: 0 0 6px;
  color: var(--text-default, #292929);
}
.wl-hero-slot__body {
  font-size: 13.5px;
  line-height: 1.5;
  margin: 0;
  color: var(--text-secondary, #565656);
}
/* FIVE TILES: 3 + 2, never a lone fifth (2026-10-10, /services gained Context Operations). A six-
   column grid where the first three span two columns and the last two span three, so the second row
   reads as a deliberate pair rather than an orphan. Scoped with :has() to exactly five tiles, so the
   four-tile pages are unchanged. */
.wl-hero-slot__tiles:has(> :nth-child(5):last-child) {
  grid-template-columns: repeat(6, minmax(0, 1fr));
}
.wl-hero-slot__tiles:has(> :nth-child(5):last-child) > * { grid-column: span 2; }
.wl-hero-slot__tiles:has(> :nth-child(5):last-child) > :nth-child(n + 4) { grid-column: span 3; }
@media (max-width: 900px) {
  .wl-hero-slot__tiles,
  .wl-hero-slot__tiles:has(> :nth-child(5):last-child) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* Both resets are needed: the desktop `:nth-child(n + 4)` span outranks a bare `> *`. */
  .wl-hero-slot__tiles:has(> :nth-child(5):last-child) > *,
  .wl-hero-slot__tiles:has(> :nth-child(5):last-child) > :nth-child(n + 4) { grid-column: auto; }
  /* The fifth tile spans the two-column tablet row instead of sitting alone in one column. */
  .wl-hero-slot__tiles:has(> :nth-child(5):last-child) > :nth-child(5) { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
  .wl-hero-slot__tiles,
  .wl-hero-slot__tiles:has(> :nth-child(5):last-child) {
    grid-template-columns: minmax(0, 1fr);
  }
  .wl-hero-slot__tiles:has(> :nth-child(5):last-child) > :nth-child(n) { grid-column: auto; }
}

/* ---- blog post, blog listing, author page (superseded 2026-09-19) -----------------------------
   The `.wl-post__*` and `.wl-blog__*` blocks that used to live here are gone. Those three templates
   now emit `@welaunch/ui`'s own class names (`wl-article-hero`, `wl-article`, `wl-blog-card`,
   `wl-topic-pills`, `wl-blog-pager`, `wl-author-hero`), so the blog is styled by the same
   stylesheet as every other page instead of by a parallel one here.

   THE REASON IS THE RULE THIS FILE ALREADY RECORDS. `theme.css` is unlayered and the library is in
   `@layer components`, so anything written here outranks the library whatever it says. A second set
   of card, chip and pagination styles for the one page that has to match the cards on every other
   page is the drift vector `themeCss.test.ts` was written for. `components/lib/blogTemplate.test.ts`
   now walks the three templates and fails when one names a `wl-` class the shipped library
   stylesheet does not define, which is what makes hand-written HubL markup safe.

   Nothing replaced them: the library carries every rule. The only blog-specific thing left in this
   file is the phone edge rule below, which is structural and cannot live in the library.
   ---------------------------------------------------------------------------------------------- */

/* ---- system and listing pages -----------------------------------------------------------------
   The nine templates added on 2026-09-18 (blog listing, landing page, 404, 500, search results,
   password prompt, subscription preferences, subscriptions confirmation, backup unsubscribe) are
   HubL, not React modules, for the same reason the blog post template is: HubSpot's system pages
   are built around default modules (`@hubspot/search_results`, `@hubspot/password_prompt`,
   `@hubspot/email_subscriptions`) whose markup we do not own and cannot wrap in a library section.
   So the design language is reproduced here in tokens rather than imported from the library.

   The shape is the library's `Section inset`: a rounded panel on the page ground, held inside the
   same 20px page inset, with the ground showing around it. Buttons are pills. Nothing here invents
   a colour; every value is a token with the same fallback the rest of this file uses.

   Class prefix is `wl-sys__` / `wl-blog__`, and neither appears in the library (field note 10).
   ---------------------------------------------------------------------------------------------- */
.wl-sys {
  padding: 120px 20px 100px;
}
.wl-sys__panel {
  max-width: 720px;
  margin: 0 auto;
  padding: 56px 48px 52px;
  background: var(--surface-raised, #FFFFFF);
  border-radius: var(--radius-card-lg, 32px);
  box-shadow: var(--shadow-panel, 0 24px 60px rgba(20, 22, 26, 0.08));
  text-align: center;
}
.wl-sys__panel--wide {
  max-width: 1120px;
  text-align: left;
}
.wl-sys__overline {
  display: block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--brand-action, #00A896);
  margin: 0 0 14px;
}
.wl-sys__title {
  font-family: var(--font-heading, "Plus Jakarta Sans", sans-serif);
  font-size: 34px;
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 14px;
  color: var(--text-heading, #292929);
}
.wl-sys__body {
  font-size: 16.5px;
  line-height: 1.6;
  margin: 0 auto 28px;
  max-width: 52ch;
  color: var(--text-secondary, #565656);
}
.wl-sys__panel--wide .wl-sys__body {
  margin-left: 0;
}
.wl-sys__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}
.wl-sys__panel--wide .wl-sys__actions {
  justify-content: flex-start;
}

/* Pill buttons for the HubL system pages. The library's Button is React and unreachable from a
   HubL template, so the two variants are rebuilt here off the same action tokens it uses.

   THE CLASS IS `wl-sys__btn`, NOT `wl-btn`, AND THAT IS THE WHOLE POINT. It was `wl-btn` until
   2026-09-18, which is the library's own Button class, and this file is UNLAYERED while the
   library lives in `@layer components`. An unlayered declaration beats a layered one at any
   specificity, so this `display: inline-flex` silently outranked the library's
   `.wl-site-header__cta { display: none }` and the header's "Book a consultation" button stayed
   visible on a phone: 189px of nowrap button in a pill that had 181px to give, so every one of
   the ten drafts and the rig scrolled 40px sideways at 390 (measured; `scripts/overflow-probe.mjs`).
   A theme file must never reuse a library class name for its own component. Overriding a library
   class from here in a COMPOUND selector is fine and deliberate (the first-band rules below do it);
   naming a NEW component after one is not. Guarded by `themeCss.test.ts`. */
.wl-sys__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 22px;
  border: 1px solid transparent;
  border-radius: var(--radius-full, 999px);
  font-family: var(--font-body, "Inter", sans-serif);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--motion-duration-fast, 140ms) var(--motion-ease-standard, ease);
}
.wl-sys__btn--primary {
  background: var(--action-primary-bg, #0066FF);
  color: var(--action-primary-text, #FFFFFF);
}
.wl-sys__btn--primary:hover {
  background: var(--state-hover-primary, #0052CC);
}
.wl-sys__btn--secondary {
  background: var(--action-secondary-bg, transparent);
  border-color: var(--action-secondary-border, #D8DBE0);
  color: var(--action-secondary-text, #292929);
}
.wl-sys__btn--secondary:hover {
  background: var(--action-secondary-bg-hover, #F2F3F5);
  color: var(--action-secondary-text-hover, #292929);
}
.wl-sys__btn:focus-visible {
  outline: 2px solid var(--border-focus, #0066FF);
  outline-offset: 2px;
}

/* HubSpot's own system modules paint their controls; give them this theme's field shape. */
.wl-sys__panel input[type="text"],
.wl-sys__panel input[type="email"],
.wl-sys__panel input[type="password"],
.wl-sys__panel input[type="search"] {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 16px;
  border: 1px solid var(--border-subtle, #D8DBE0);
  border-radius: var(--radius-field, 12px);
  background: var(--surface-page, #F9F9F9);
  color: var(--text-default, #292929);
  font-family: var(--font-body, "Inter", sans-serif);
  font-size: 15px;
}
.wl-sys__panel input:focus-visible {
  outline: 2px solid var(--border-focus, #0066FF);
  outline-offset: 1px;
}
.wl-sys__panel input[type="submit"],
.wl-sys__panel button[type="submit"],
.wl-sys__panel .hs-button {
  margin-top: 14px;
  padding: 12px 22px;
  border: 0;
  border-radius: var(--radius-full, 999px);
  background: var(--action-primary-bg, #0066FF);
  color: var(--action-primary-text, #FFFFFF);
  font-family: var(--font-body, "Inter", sans-serif);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
.wl-sys__panel .hs-search-results__title {
  font-family: var(--font-heading, "Plus Jakarta Sans", sans-serif);
  color: var(--text-heading, #292929);
  text-decoration: none;
}
.wl-sys__panel .hs-search-results__listing {
  list-style: none;
  margin: 0;
  padding: 0;
}
.wl-sys__panel .hs-search-results__listing li {
  padding: 20px 0;
  border-bottom: 1px solid var(--border-subtle, #D8DBE0);
  text-align: left;
}

/* ---- system page phone rules (kept from the retired blog-listing block) -----------------------
   These three `.wl-sys__*` rules were inside the old blog-listing block's media query. They belong
   to the system pages (404, 500, search, subscription preferences) and are the only thing that
   survived it.
   ---------------------------------------------------------------------------------------------- */
@media (max-width: 660px) {
  .wl-sys {
    padding: 96px 16px 72px;
  }
  .wl-sys__panel {
    padding: 40px 24px 36px;
  }
  .wl-sys__title {
    font-size: 27px;
  }
}

/* ---- WL Form Panel: MOVED TO THE LIBRARY, 2026-09-21 ------------------------------------------
   The panel, the field shapes, the pill submit and the phone rules used to be here, ~145 lines of
   them, and that was the bug rather than the tidy-up. This file is UNLAYERED and the library ships
   inside `@layer components`, so an unlayered rule outranks the library at any specificity (field
   note 24). Once `FormPanel` became a library section, every selector here would have silently
   beaten its own component's stylesheet, and `themeCss.test.ts` fails on exactly that shape: a
   bare library class defined in this file.

   They now live in `packages/ui/src/sections/FormPanel/FormPanel.css`, layered, with the cascade
   reasoning written out there (including why a handful of declarations carry `!important`: they
   are the properties HubSpot's own injected form <style> also sets, and importance is compared
   before layers). Nothing about the HubSpot-markup problem changed; only which stylesheet owns it.
   ---------------------------------------------------------------------------------------------- */

/* ---- the meetings embed, as a panel in BOTH themes ---------------------------------------------
   The HubSpot meetings embed is a cross-origin iframe (meetings.hubspot.com): its interior can
   never be styled from here. What can be styled is its box, and since 2026-09-21 it gets the same
   construction as every other panel on the site in both themes (Conner: "the wrap sounds great"):
   the 1120 column, the panel radius, the card shadow, the page ground showing around it. Until then
   this was dark-only (a dark sweep remedy, 2026-09-19); the light page had been reviewed with the
   embed full bleed, and it was left alone. Now the light page has been reviewed with the panel.

   On the thank-you page the calendar is the optional last section since 2026-09-21 (access via the
   partner invite link comes first), so it sits in flow like the Contact page's; the band overhang it
   briefly had is gone. */
.meetings-iframe-container {
  position: relative;
  max-width: 1120px;
  margin: 0 auto;
  border-radius: var(--radius-card-lg, 28px);
  overflow: clip;
  box-shadow: var(--shadow-card);
  background: #ffffff;
}
/* Below the column width the page inset is what holds it off the edge, exactly as `Section inset`
   does. Without it the panel is flush at both edges and the radius is drawn on nothing. */
@media (max-width: 1160px) {
  .meetings-iframe-container { max-width: calc(100% - 40px); }
}

/* HubSpot cookie banner: stop iOS Safari jumping to the footer on first visit (2026-10-10).
   banner.js appends `#hs-banner-parent` (position: static, zero height) as the last child of <body>,
   then calls focus() on the position: fixed `#hs-eu-cookie-confirmation` inside it without
   preventScroll. WebKit scrolls to the fixed element's static position, which is the end of the
   page, so a visitor with no consent cookie yet (every private window, every first visit) lands on
   the footer. Reproduced in Playwright WebKit at iPhone 14 Pro size: scrollY 0 -> 7005 right after
   the focus call; focus with preventScroll, or this rule, keeps it at 0. Chromium does not scroll.
   Pinning the parent to the top of the viewport moves that static position to the top. The
   z-index keeps the banner above the hero, whose layers otherwise paint over it once the parent
   is a fixed stacking context. Zero size, so it never blocks clicks on the page. */
#hs-banner-parent {
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  z-index: 2147483000;
}

/* Links inside the HubSpot cookie banner (the Privacy Policy link in the notification text).
   HubSpot styles its own buttons but leaves inline links to the browser default (#0000EE). The
   banner card is white in both themes, so the -on-light token, which does not flip in dark mode,
   is the right partner. Matches the banner's own "Cookies settings" button (#0854C5). */
#hs-eu-cookie-confirmation p a {
  color: var(--text-link-on-light, #0854C5);
  text-decoration: underline;
  text-underline-offset: 2px;
}
#hs-eu-cookie-confirmation p a:hover {
  text-decoration-thickness: 2px;
}

/* THE BLOG'S FEATURED POST KEEPS ITS ROOM UNDER THE LISTING HERO, WHATEVER THE HERO WAS TOLD
   (incident, build #92, 2026-10-12). The featured post on the listing's page one is a Slot that
   rises 104px into the hero band. `blog-listing.hubl.html` passes `overlap_room` so the hero keeps
   its phone bottom reserve when the card renders; on #92 the value reached the module wrong and
   the card covered the subhead at 390 and 820. This restores the library's slotted numbers (156px
   bottom padding, 32px under the subhead) on any page that renders the card, so a template value
   that goes astray can only ever cost the gap, never an overlap.
   Keyed on `.wl-blog-featured` and nothing wider: it exists only on listing page one and always
   straddles the hero. Not `.wl-slot` or `.wl-form-panel`, because the topic listings carry a
   FormPanel far down the page and would then never tighten. Unlayered, so it outranks the
   library's `--no-slot` rule; phone tier only, like the rule it guards. */
@media (max-width: 900px) {
  .wl-page:has(.wl-blog-featured) .wl-hero-mesh--wash.wl-hero-mesh--center.wl-hero-mesh--no-slot { padding-bottom: 156px; }
  .wl-page:has(.wl-blog-featured) .wl-hero-mesh--wash.wl-hero-mesh--center.wl-hero-mesh--no-slot .wl-hero-mesh__copy > :last-child { margin-bottom: 32px; }
}
