/* ==========================================================================
   Landing page preview — dedicated, narrowly-scoped stylesheet.
   Reuses brand tokens (--brand, --brand-dark, --bg, --ink, --muted, --border)
   and shared form/button classes already defined in base.css. Does not
   touch base.css/home.css so other pages are unaffected.
   ========================================================================== */

/* -----------------------------
   Motion language
   -----------------------------
   Four purposes, four durations, two curves. This file is loaded by the
   landing page and every service-detail page, so :root here covers both.

     fast    micro-interaction — hover, focus, arrows, the call control
     ui      state + navigation change, and soft media reveals
     reveal  composed section entrance (heading groups, grids)
     hero    the hero composition, and media dissolves

   One stagger step is used everywhere a group settles in sequence, so the
   hero, a section heading and the Support grid all share a rhythm rather
   than each having its own. Anything that does not fit one of these four
   purposes should not be animating.

   --ease-premium decelerates hard: content resolves into place rather than
   flying in. --ease-ui is the flatter curve for interaction feedback. */
:root {
  --motion-fast: 180ms;
  --motion-ui: 300ms;
  --motion-reveal: 480ms;
  --motion-hero: 540ms;
  --motion-step: 50ms;
  /* A tighter step for cascading within a single list, where the full step
     would stretch six items over a third of a second. */
  --motion-step-fine: 35ms;

  /* Entrance durations, one per role. Named for the job rather than numbered,
     and deliberately unequal: the four finishing at different times is what
     makes the sequence read as layered rather than metronomic. Longer than the
     interaction durations above because entrance travel has to be legible —
     at the distances used here a 300ms move is a flicker, not a movement. */
  --motion-in-label: 500ms;
  --motion-in-action: 560ms;
  --motion-in-body: 640ms;
  --motion-in-card: 680ms;
  --motion-in-title: 720ms;

  --ease-ui: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-premium: cubic-bezier(0.22, 1, 0.36, 1);
  /* A short overshoot for things that should feel like they land:
     controls and panels. Deliberately mild — it settles, it does
     not bounce. Never used on type. */
  --ease-settle: cubic-bezier(0.34, 1.32, 0.64, 1);
}

.lp-page {
  --lp-dark-bg: var(--bg);
  --lp-dark-ink: var(--ink);
  --lp-dark-muted: var(--muted);
  --lp-dark-border: var(--border);

  /* Off-white editorial surface — same authoritative hexes as base.css's
     light-mode :root tokens, applied explicitly since <html> forces dark-mode. */
  --lp-light-bg: #ecf2f3;
  --lp-light-ink: #0b1521;
  --lp-light-muted: #4d5b6b;
  --lp-light-border: rgba(8, 47, 73, 0.14);

  /* Accent for *text* on the light surface. The marine blue used everywhere
     else (--brand / --brand-dark, #38bdf8 / #0ea5e9) measures 2.45:1 here,
     well under the 4.5:1 WCAG AA needs for normal text — fine as a graphic or
     on navy, unreadable as small type on off-white.

     Deliberately not --brand-800: base.css redefines that token inside its
     .dark-mode block to #0284c7, which still only reaches 3.62:1, so the
     obvious-looking fix would have silently failed. These two are named for
     the surface they belong to so they cannot be shadowed.

     #0369a1 = 5.24:1, #075985 = 6.68:1 against #ecf2f3. */
  --lp-light-accent: #0369a1;
  --lp-light-accent-strong: #075985;

  /* Editorial mono face — used only for the numbered/spec-sheet labels in
     the Sectors, Our Work and Feedback sections below. */
  --font-mono: "DM Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  /* Single source of truth for "how far below the fixed navbar a jump-
     navigated section's heading sits" — every section a nav link can jump
     to (Services/Capabilities, Sectors, Storage, About) reads this same
     value, instead of each one independently hand-tuning its own top
     offset and drifting out of sync with the others over time. */
  --lp-jump-pad: clamp(32px, 6vw, 72px);

  overflow-x: clip;
}
@supports not (overflow-x: clip) {
  .lp-page { overflow-x: hidden; }
}
@media (min-width: 900px) and (max-height: 800px) {
  .lp-page { --lp-jump-pad: clamp(20px, 3vw, 32px); }
}

.lp-section {
  min-height: 100vh;
  min-height: 100svh;
  padding: clamp(48px, 7vw, 96px) 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* .lp-section's min-height:100vh earns its keep where a section is a
   composition — the hero, the full-bleed carousel. Where it is a column of
   text it just pads the section out to the window: at 1440x900 it left 170px
   of empty above and below the reviews, and 388px under the About copy.
   These three size to their own content and their padding instead. */
#feedback.lp-section,
#about.lp-section {
  min-height: auto;
}

.lp-light-section {
  background: var(--lp-light-bg);
  color: var(--lp-light-ink);
}
.lp-light-section .cme-section-title { color: var(--lp-light-ink); }
.lp-light-section .cme-section-subtitle,
.lp-light-section .lp-sectors__lede { color: var(--lp-light-muted); }
.lp-light-section .cme-eyebrow { color: var(--lp-light-accent); }

.lp-dark-section {
  background: var(--lp-dark-bg);
  color: var(--lp-dark-ink);
}
.lp-dark-section .cme-section-subtitle { color: var(--lp-dark-muted); }
/* Without this the enquiry band's eyebrow fell through to base.css's muted
   grey while every other eyebrow on the page was brand blue. */
.lp-dark-section .cme-eyebrow { color: var(--brand-light, #38bdf8); }

/* -----------------------------
   Hero
   ----------------------------- */
.lp-hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  color: #f4f7fb;
  padding-top: var(--nav-h, 90px);
}

/* .cme-hero__bg is shared with the old homepage, where base.css deliberately
   offsets it ("right 6%/10% center") to suit that page's own background
   image. Here the div only ever holds the active clip's poster, painted
   underneath <video> elements that use object-position: 50% 50% — so the
   inherited offset made the still frame and the footage crop to different
   parts of the scene. Most visible on phones, where the tall viewport crops
   ~1000px horizontally and a 6% shift moves the framing a long way.
   Re-centred here only; the homepage hero is untouched. */
/* Media dissolve, matching the capability carousel and the service-detail
   hero. base.css cuts between hero clips in 200ms, which reads as a blink
   once the clips are this slow and this dark; scoped here rather than changed
   there so the existing homepage hero keeps its own timing. */
.lp-hero .cme-hero__video {
  transition: opacity var(--motion-hero) var(--ease-ui);
}

.lp-hero .cme-hero__bg {
  background-position: center center;
}

.lp-hero__scrim {
  position: absolute;
  inset: 0;
  background:
    /* The tail was 0.92, which crushed the foot of the hero to a flat block
       of scrim colour — footage, and any detail in it, disappeared for the
       last ~40px and read as a gap above the next section. 0.60 keeps the
       copy legible (white on this measures 5.4:1, above the 4.5:1 AA floor)
       while letting the footage run to the bottom edge. */
    linear-gradient(180deg, rgba(6, 16, 34, 0.55) 0%, rgba(6, 16, 34, 0.35) 35%, rgba(6, 16, 34, 0.60) 100%),
    linear-gradient(90deg, rgba(6, 16, 34, 0.75) 0%, rgba(6, 16, 34, 0.15) 60%);
}

/* -----------------------------
   Hero entrance
   -----------------------------
   The hero's own copy rises in on load, staggered top to bottom, so the page
   resolves in a deliberate order rather than appearing all at once over a
   video that is still buffering.

   Deliberately CSS-only. A JS-gated reveal has a failure mode this cannot
   have: if the script never runs, the content stays hidden forever. Here the
   animation is the only thing that can fail, and `backwards` fill means the
   pre-delay frame is the from-state — so with animations unsupported the copy
   simply renders normally, visible and in place.

   Only opacity and transform are animated: both are compositor-only, so this
   cannot move layout or contribute to CLS. Nothing is gated on it — every
   link and button is clickable from first paint. */
/* -----------------------------
   Entrance roles
   -----------------------------
   Four motions, chosen by what an element *is* rather than where it sits in
   its parent. A label, a headline, a paragraph and a control are different
   kinds of thing, and a single rise-in applied to all of them at evenly
   spaced delays reads as a list loading rather than a composition arriving.

     label   a small tracked tag. Drifts in from the side and is quickly done —
             it is being placed beside the content, not lifted into it.
     title   the weight of the page. Least travel, longest duration, hardest
             deceleration, so large type resolves rather than flies.
     body    prose. Barely moves at all; it surfaces.
     action  a control becoming available. Most travel, snappiest curve.
     card    a panel settling, with a whisper of scale text should never have.

   Durations differ on purpose (300/540/480/300ms), so the four finish at
   staggered times even where their delays are close. That overlap is what
   makes it read as layered instead of metronomic.

   The same mapping drives the hero and every scroll-revealed group, so a
   heading behaves identically wherever it appears. `backwards` fill means the
   pre-delay frame is the from-state: if animations never run, everything
   simply renders in place. Only opacity and transform are animated — both
   compositor-only, so none of this can move layout or cost CLS. */
@keyframes lp-in-label {
  from { opacity: 0; transform: translateX(-26px); }
  35%  { opacity: 1; }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes lp-in-label-right {
  from { opacity: 0; transform: translateX(26px); }
  35%  { opacity: 1; }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes lp-in-title {
  from { opacity: 0; transform: translateY(36px); }
  30%  { opacity: 1; }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes lp-in-body {
  from { opacity: 0; transform: translateY(26px); }
  35%  { opacity: 1; }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes lp-in-action {
  from { opacity: 0; transform: translateY(22px) scale(0.94); }
  30%  { opacity: 1; }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes lp-explore-in {
  from { opacity: 0; transform: translate(-50%, 26px); }
  35%  { opacity: 1; }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@keyframes lp-in-card {
  from { opacity: 0; transform: translateY(42px) scale(0.97); }
  25%  { opacity: 1; }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* The two scopes that animate their children: the hero on load, and any
   revealed group on scroll. Listed once here and reused by every role rule. */
.lp-hero__content > *,
.lp-svc-hero__content > *,
.lp-error__content > *,
.lp-legal__head > *,
.lp-svc-hero__topbar,
/* :where() so the two exclusions cost nothing. Written as plain :not() they
   added two classes to this selector, lifting it above every role rule below
   and silently collapsing the whole system back to one motion: labels,
   headings, cards and stacks all animated as body copy. */
.lp-reveal-ready .lp-reveal:where(:not(.lp-reveal--soft, .lp-capability-panels)).is-revealed > * {
  opacity: 1;
  /* Body is the default: anything unclassified is prose. */
  animation: lp-in-body var(--motion-in-body) var(--ease-premium)
             calc(var(--motion-step) * 2) backwards;
}

.lp-hero__content > :is(.lp-eyebrow, .cme-eyebrow, .lp-editorial-head__eyebrow),
.lp-error__content > .cme-eyebrow,
.lp-legal__head > .cme-eyebrow,
.lp-svc-hero__topbar,
.lp-reveal-ready .lp-reveal.is-revealed > :is(.lp-eyebrow, .cme-eyebrow, .lp-editorial-head__eyebrow, .lp-bitcoin__mark) {
  animation-name: lp-in-label;
  animation-duration: var(--motion-in-label);
  animation-timing-function: var(--ease-premium);
  animation-delay: 0ms;
}

.lp-hero__content > :is(h1, h2, h3, .cme-section-title),
.lp-svc-hero__content > :is(h1, h2, h3, .cme-section-title),
.lp-error__content > :is(h1, h2, h3),
.lp-legal__head > :is(h1, h2, h3),
.lp-reveal-ready .lp-reveal.is-revealed > :is(h1, h2, h3, .cme-section-title) {
  animation-name: lp-in-title;
  animation-duration: var(--motion-in-title);
  animation-delay: calc(var(--motion-step) * 1);
}

/* The rating sits with the buttons on one beat: a star rating arriving before
   its own CTAs draws the eye to the wrong thing. */
.lp-hero__content > :is(.lp-hero__rating, .lp-hero__actions, .lp-mobile-service__actions),
.lp-bitcoin__copy > .lp-bitcoin__actions,
.lp-svc-hero__content > .lp-svc-hero__actions,
.lp-error__content > .lp-error__actions,
.lp-reveal-ready .lp-reveal.is-revealed > :is(.lp-hero__rating, .lp-hero__actions, .lp-mobile-service__actions, .lp-support-fallback__actions, .lp-bitcoin__actions) {
  animation-name: lp-in-action;
  animation-duration: var(--motion-in-action);
  animation-timing-function: var(--ease-settle);
  animation-delay: calc(var(--motion-step) * 5);
}

/* The scope line is a second paragraph under the lede, not a peer of it, so
   it takes the next beat rather than arriving on the same one. Rapid
   Response's availability pair sits in the same position and reads the same
   way, so it joins the same beat rather than introducing another — the two
   lines are one statement and must not arrive separately from each other. */
.lp-svc-hero__content > :is(.lp-svc-hero__scope, .lp-svc-hero__availability, .lp-svc-hero__availability-note) {
  animation-delay: calc(var(--motion-step) * 3);
}

/* Bottom scroll cue, arriving after the main hero actions. */
.lp-hero > .lp-hero__explore {
  animation: lp-explore-in var(--motion-in-action) var(--ease-settle)
             calc(var(--motion-step) * 7) backwards;
}

/* -----------------------------
   Capabilities carousel entrance
   -----------------------------
   This section already owns three motion systems: the panels slide left/right
   on tab change, the background clips crossfade, and the active panel carries
   view-transition-name for the landing -> service-detail continuity. So the
   entrance deliberately animates the chrome and the *contents* of the visible
   panel, and never the panel elements themselves — putting a reveal on those
   would fight the slide and risk leaving a hidden panel's copy stuck at zero
   opacity when its tab is later selected. */

/* The hint sits at the right end of its row, so it arrives from the right.
   Same motion as a label, mirrored — a left drift would read as sliding away
   from its own edge. */
.lp-reveal-ready .lp-reveal.is-revealed > .lp-capability-topbar__hint {
  animation-name: lp-in-label-right;
  animation-duration: var(--motion-in-label);
  animation-timing-function: var(--ease-premium);
  animation-delay: calc(var(--motion-step) * 1);
}

/* The panel articles are excluded from the base reveal rules above by
   :not(.lp-capability-panels), rather than being reset here afterwards. An
   `animation: none` reset outranks the .lp-capability-panel--enter-* classes
   the tab switcher applies, so it silently killed the slide. */

/* Only the panel actually on screen animates, and only the once. */
.lp-reveal-ready .lp-capability-panels.lp-reveal:not(.is-revealed) .lp-capability-panel:not([hidden]) > * {
  opacity: 0;
}
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > * {
  opacity: 1;
  animation: lp-in-body var(--motion-in-body) var(--ease-premium)
             calc(var(--motion-step) * 2) backwards;
}
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > h3 {
  animation-name: lp-in-title;
  animation-duration: var(--motion-in-title);
  animation-delay: calc(var(--motion-step) * 1);
}
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > .lp-capability-story__actions {
  animation-name: lp-in-action;
  animation-duration: var(--motion-in-action);
  animation-timing-function: var(--ease-settle);
  animation-delay: calc(var(--motion-step) * 5);
}

/* The capability list cascades item by item rather than arriving as one block —
   the closest thing on the page to reading down a spec sheet. The <ul> itself
   stays still so the two transforms never compound. */
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul {
  animation: none;
  opacity: 1;
}
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li {
  animation: lp-in-body var(--motion-in-body) var(--ease-premium) backwards;
}
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(1) { animation-delay: calc(var(--motion-step) * 2); }
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(2) { animation-delay: calc(var(--motion-step) * 2 + var(--motion-step-fine) * 1); }
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(3) { animation-delay: calc(var(--motion-step) * 2 + var(--motion-step-fine) * 2); }
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(4) { animation-delay: calc(var(--motion-step) * 2 + var(--motion-step-fine) * 3); }
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(5) { animation-delay: calc(var(--motion-step) * 2 + var(--motion-step-fine) * 4); }
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(6) { animation-delay: calc(var(--motion-step) * 2 + var(--motion-step-fine) * 5); }
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(7) { animation-delay: calc(var(--motion-step) * 2 + var(--motion-step-fine) * 6); }
.lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li:nth-child(8) { animation-delay: calc(var(--motion-step) * 2 + var(--motion-step-fine) * 7); }

/* --stack: a group of peers rather than a composition.

   Where .lp-reveal's children are an eyebrow, a heading and a paragraph, the
   role system gives each its own motion. Where they are three columns, three
   review rows or six feature points, they are the same kind of thing and
   should arrive as one wave — same motion, stepped by index. */
.lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > * {
  animation-name: lp-in-card;
  animation-duration: var(--motion-in-card);
  animation-timing-function: var(--ease-settle);
}
.lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(1) { animation-delay: 0ms; }
.lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(2) { animation-delay: calc(var(--motion-step) * 1); }
.lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(3) { animation-delay: calc(var(--motion-step) * 2); }
.lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(4) { animation-delay: calc(var(--motion-step) * 3); }
.lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(5) { animation-delay: calc(var(--motion-step) * 4); }
.lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(6) { animation-delay: calc(var(--motion-step) * 5); }


@media (prefers-reduced-motion: reduce) {
  /* Every role selector is repeated at its own specificity. A single
     `.lp-hero__content > *` reset looks tidier but loses: each role rule
     carries an extra class, so the animation ran anyway. */
  .lp-hero__content > *,
  .lp-hero__content > :is(.lp-eyebrow, .cme-eyebrow, .lp-editorial-head__eyebrow),
  .lp-hero__content > :is(h1, h2, h3, .cme-section-title),
  .lp-hero__content > :is(.lp-hero__rating, .lp-hero__actions, .lp-mobile-service__actions),
  .lp-bitcoin__copy > .lp-bitcoin__actions,
  .lp-hero > .lp-hero__explore,
  .lp-svc-hero__content > *,
  .lp-svc-hero__content > :is(h1, h2, h3, .cme-section-title),
  .lp-svc-hero__content > .lp-svc-hero__actions,
  .lp-svc-hero__content > :is(.lp-svc-hero__scope, .lp-svc-hero__availability, .lp-svc-hero__availability-note),
  .lp-svc-hero__topbar,
  .lp-error__content > *,
  .lp-error__content > .cme-eyebrow,
  .lp-error__content > :is(h1, h2, h3),
  .lp-error__content > .lp-error__actions,
  .lp-legal__head > *,
  .lp-legal__head > .cme-eyebrow,
  .lp-legal__head > :is(h1, h2, h3) {
    animation: none;
    opacity: 1;
    transform: none;
  }
}


.lp-hero__content {
  position: relative;
  padding-top: clamp(64px, 14vw, 140px);
  padding-bottom: 40px;
  /* .cme-section-container's centring is kept; the 720px measure moved onto
     the children. Overriding max-width here instead pinned the copy to the
     viewport's left gutter, so above ~1340px the headline started 80px left
     of the navbar wordmark directly above it and of every section heading
     below it. */
  max-width: calc(var(--cme-section-maxw) + (var(--cme-section-gx) * 2));
  margin-inline: auto;
}
.lp-hero__content > * { max-width: 720px; }

/* base.css pins this 20px from the viewport edge, which was fine when the
   hero copy was pinned there too. Now that the copy sits on the page grid the
   chip has to as well, or it hangs 80px outside the column at 1440px. */
.lp-hero .cme-hero__trust {
  right: max(var(--cme-section-gx), calc((100% - var(--cme-section-maxw)) / 2));
}

.lp-eyebrow {
  /* "Scotland's marine engineering specialist" wraps at 360-390px; balanced,
     it splits 2/2 instead of leaving "SPECIALIST" alone on its own line.
     Cheaper than shrinking the type to force one line. */
  text-wrap: balance;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--brand);
  margin: 0 0 16px;
}

.lp-hero__title {
  font-size: clamp(2.2rem, 5vw, 3.4rem);
  line-height: 1.08;
  font-weight: 700;
  margin: 0 0 20px;
  max-width: 15ch;
}

.lp-hero__lede {
  font-size: 1.08rem;
  line-height: 1.6;
  color: #d7e0ec;
  max-width: 52ch;
  margin: 0 0 22px;
}

/* Rating — a quiet trust signal bridging the proposition above it into the
   enquiry actions below. Deliberately plain (no chip/border/shadow) so it
   reads as a fact, not another button. */
.lp-hero__rating {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 26px;
  font-size: 0.85rem;
  font-weight: 500;
  color: rgba(244, 247, 251, 0.82);
}
.lp-hero__rating-stars {
  color: #e3c169;
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  line-height: 1;
}

.lp-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

/* Homepage version of the service-detail hero exploration cue: navigation,
   not a third conversion CTA. No fill, border, pill or circle; the small
   uppercase label and chevron match the detail-page hero affordance. */
.lp-hero__explore {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: max(20px, env(safe-area-inset-bottom, 0px) + 16px);
  transform: translate(-50%, 0);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 10px 16px;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brand, #38bdf8);
  transition: color var(--motion-fast) var(--ease-ui);
}
.lp-hero__explore-label {
  display: inline-block;
}
.lp-hero__explore-label::after {
  content: "";
  display: block;
  height: 2px;
  margin-top: 4px;
  border-radius: 999px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--motion-fast) var(--ease-ui);
}
.lp-hero__explore-arrow {
  font-size: 0.78rem;
  line-height: 1;
  color: var(--brand, #38bdf8);
  transition: transform var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui);
}
.lp-hero__explore:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 6px;
  border-radius: 4px;
  color: var(--brand, #38bdf8);
}
.lp-hero__explore:focus-visible .lp-hero__explore-label::after {
  transform: scaleX(1);
}


/* -----------------------------
   Bitcoin payments — quiet, centred trust strip (not a promotional
   banner). Base colour is the site's own dark-mode navy (--lp-dark-bg,
   i.e. var(--bg)) with a very low-opacity blue glow and an even fainter
   engineering grid layered on top — orange is reserved for the Bitcoin
   mark only, everything else stays inside the navy/blue/white system.
   ----------------------------- */
.lp-bitcoin-note {
  background:
    radial-gradient(900px 420px at 50% -10%, color-mix(in srgb, var(--brand) 10%, transparent), transparent 70%),
    var(--lp-dark-bg);
  border-top: 1px solid var(--lp-dark-border);
  border-bottom: 1px solid var(--lp-dark-border);
  /* This section is only ever reached by anchor jump (hero trust chip, or a
     direct #bitcoin-note link), so where it lands is the only view of its top
     edge most readers get.

     Zero, not var(--nav-h). The navbar is translucent, so landing the
     section at the bar's *bottom* edge leaves the 90px behind the glass
     still showing the white section above — the jump looks like it stopped
     short. Landing at the viewport top instead runs the navy up behind the
     bar and fills it.

     Same reasoning and same fix as #core-services on the service pages,
     which spells it out: "it runs up behind the translucent navbar rather
     than stopping short and leaving a strip of the hero showing above it."

     Clearance then becomes padding-top's job entirely, which is why it
     carries var(--nav-h) below. */
  scroll-margin-top: 0;
}

/* One centred column. This was a two-column grid with a step panel on the
   right; with the panel gone there is nothing to balance against, and a lone
   left-aligned column in a 1200px container just reads as a layout with a
   hole in it. Centred, the mark sits directly over the headline it belongs
   to and the whole section resolves as one short statement. */
.lp-bitcoin__grid {
  display: grid;
  justify-items: center;
  text-align: center;
  /* Top and bottom deliberately unequal, and the top now carries the navbar
     height itself: the section lands at the viewport top (scroll-margin-top:
     0 above), so the first 90px of it sits behind the glass bar and only
     what follows is visible. Without var(--nav-h) in here the mark would
     land 6px under the bar.

     The clamp on top of it is the gap the reader actually sees — the same
     one that was there before this changed, so the composition is unmoved.
     The bottom has no fixed bar over it and keeps the original rhythm. */
  padding-block: calc(var(--nav-h, 90px) + clamp(104px, 12vw, 168px)) clamp(64px, 9vw, 120px);
}

.lp-bitcoin__copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 60ch;
}

/* -----------------------------
   The mark
   ----------------------------- */

/* The section's focal point, and the only orange on the page. It opens the
   column above the eyebrow, so it is read as the subject before anything is
   said — which is why it can afford to be this size.

   No plate behind it. The earlier tinted square was there to give a bare ₿
   character some structure; the real logo is already a closed circular shape
   and boxing it just added a second, competing edge.

   Softening. #f7931a is a high-chroma orange and this navy is nearly
   chroma-free, so at full strength the mark glared — the eye went to it and
   stopped, instead of carrying on into the headline. Two things were doing
   that: the saturation itself, and the hard edge where a flat disc meets a
   flat ground with nothing in between.

   saturate() takes the glare off while leaving it unmistakably Bitcoin
   orange — pulled further (0.6 and below, tested) it goes tan and stops
   reading as the brand at all. The two drop-shadows handle the edge: a tight
   bloom that lets the disc dissolve outward instead of stopping dead, and a
   wide low one that lifts it off the ground. drop-shadow rather than
   box-shadow, which would trace the element's square box rather than the
   circle. The colours are the mark's own, so the ground picks up its light
   rather than a grey halo. */
.lp-bitcoin__mark {
  display: block;
  width: clamp(78px, 7.4vw, 104px);
  height: auto;
  margin-bottom: clamp(22px, 2.6vw, 30px);
  filter:
    saturate(0.78)
    brightness(0.96)
    drop-shadow(0 0 22px rgba(247, 147, 26, 0.24))
    drop-shadow(0 18px 46px rgba(247, 147, 26, 0.14));
}

.lp-bitcoin__title {
  margin: 0 0 10px;
  /* "with&nbsp;Bitcoin" in the markup keeps the phrase together, so the line
     breaks before it rather than orphaning the word. */
  font-size: clamp(1.75rem, 3.4vw, 2.6rem);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--lp-dark-ink);
}

/* Footnote marker, not punctuation: raised and reduced so it reads as a
   reference rather than as part of the word, and tinted with the section's
   own accent so the eye connects it to the matching mark below without
   either one having to shout. Sized in em so it tracks the headline's clamp
   rather than needing its own. */
.lp-bitcoin__title-mark {
  font-size: 0.42em;
  vertical-align: 0.62em;
  margin-left: 0.08em;
  font-weight: 600;
  color: var(--brand);
}

/* Deliberately quiet, and now at the foot of the section rather than under
   the headline. Not a badge, not a banner and not an alert colour — this is
   product information about which jobs are eligible, and styling it as a
   warning would make an ordinary condition look like a catch.

   The margin is asymmetric to the facts grid above it: far enough to read as
   a footnote to the whole section rather than a fifth bullet, close enough
   that it is plainly still attached to it. */
.lp-bitcoin__qualifier {
  margin: clamp(28px, 3.4vw, 44px) 0 0;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--lp-dark-muted);
}

.lp-bitcoin__qualifier-mark {
  margin-right: 0.32em;
  font-weight: 600;
  color: var(--brand);
}

.lp-bitcoin__lede {
  margin: 0;
  max-width: 52ch;
  font-size: 1.02rem;
  line-height: 1.7;
  color: var(--lp-dark-muted);
}

.lp-bitcoin__actions { margin-top: clamp(24px, 3vw, 32px); }

/* -----------------------------
   The six answers
   -----------------------------
   Sibling of .lp-bitcoin__copy rather than a child of it, because the copy
   column is capped at 60ch — right for a paragraph, far too narrow for three
   columns of anything.

   Left-aligned inside a centred section. Centred running text is fine for
   two sentences under a headline; six short blocks of it read as a wobbly
   edge with nothing to scan down. The block as a whole is still centred, so
   the section keeps its axis. */
.lp-bitcoin__facts {
  list-style: none;
  margin: clamp(36px, 4.5vw, 56px) 0 0;
  padding: 0;
  width: 100%;
  /* Two columns, not four. Four across fits the container, but "The amount
     is set when payment falls due" wraps at that width while the other three
     do not, so one column ends up a line taller than its neighbours and the
     row stops looking like a row. */
  max-width: 780px;
  text-align: left;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(18px, 2vw, 24px) clamp(28px, 3.5vw, 48px);
}

/* Icon and label on one row. The explanatory sentence under each of these
   is gone, so the label has to carry the point on its own — which is why
   they stayed full phrases ("Refunds follow the invoice") rather than being
   cut to nouns ("Refunds"), where the icon would have been doing work it
   cannot do.

   align-items: start, not center: two of the labels wrap to a second line
   at narrow widths, and centred the icon would drift to the middle of the
   block instead of sitting against the first line where it reads as a
   marker. */
.lp-bitcoin__fact {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  align-items: start;
  gap: 12px;
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--lp-dark-ink);
}

/* Blue, not orange. Orange is the mark's alone in this section; six orange
   icons under it would spread the accent around and cost the logo its job. */
.lp-bitcoin__fact i {
  /* Optical centring on the first line: the glyph box is shorter than the
     line box, so without the nudge it rides high against the cap height. */
  margin-top: 0.16em;
  font-size: 0.98rem;
  color: var(--brand);
  justify-self: center;
}

/* -----------------------------
   Mobile
   ----------------------------- */
@media (max-width: 640px) {
  .lp-bitcoin__facts {
    grid-template-columns: 1fr;
    gap: 22px;
  }
  /* The grid reads as texture at desktop scale; on narrow screens it
     compresses into visual noise, so it's dropped and only the soft
     glow + flat navy remain. */
  .lp-bitcoin-note {
    background:
      radial-gradient(700px 320px at 50% -10%, color-mix(in srgb, var(--brand) 9%, transparent), transparent 70%),
      var(--lp-dark-bg);
  }
  /* Asymmetric on purpose. The navbar overlays this section during an
     ordinary scroll as well as on an anchor jump, and at this width the
     symmetric padding left the logo close under it. */
  .lp-bitcoin__grid {
    padding-block: calc(var(--nav-h, 90px) + clamp(96px, 24vw, 128px)) clamp(48px, 12vw, 64px);
  }
  .lp-bitcoin__mark { width: 72px; margin-bottom: 20px; }
  .lp-bitcoin__actions { align-self: stretch; }
  .lp-bitcoin__actions .lp-btn { width: 100%; }
}

/* -----------------------------
   Buttons / links (scoped variants)
   ----------------------------- */
.lp-btn {
  --lp-btn-h: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--lp-btn-h);
  padding: 0 22px;
  border-radius: 6px;
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.15;
  text-decoration: none;
  border: 1px solid transparent;
  transition: transform var(--motion-fast) var(--ease-ui), box-shadow var(--motion-fast) var(--ease-ui), background var(--motion-fast) var(--ease-ui);
}

.lp-btn--lg { --lp-btn-h: 52px; padding-inline: 24px; font-size: 1rem; }
.lp-btn--sm { --lp-btn-h: 40px; padding-inline: 16px; font-size: 0.85rem; }

.lp-btn--primary {
  background: linear-gradient(180deg, var(--brand), var(--brand-dark));
  /* Deep navy label, not white. White on this gradient measured 2.26-2.46:1,
     the site's last WCAG 1.4.3 failure. The blue is the brand and stays
     exactly as it is; only the ink on it changes. 5.58-7.23:1 across the
     gradient. */
  color: #06263f;
  box-shadow: 0 6px 14px rgba(2, 132, 199, 0.18);
}
.lp-btn--ghost {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.35);
  color: #f4f7fb;
}

.lp-btn--light {
  background: #fff;
  color: var(--brand-dark);
  border-color: rgba(0, 0, 0, 0.08);
}

.lp-nav__cta-item { display: flex; align-items: center; }

@media (min-width: 992px) {
  .lp-nav .navbar-nav {
    gap: clamp(0.75rem, 1.25vw, 1.35rem);
  }

  .lp-nav .nav-link {
    padding-inline: clamp(0.45rem, 0.75vw, 0.65rem);
  }

  .lp-nav .lp-btn--sm {
    padding-inline: clamp(12px, 1vw, 16px);
  }
}

.lp-link {
  /* Light-surface default. .lp-dark-section / .lp-quote-band override to the
     brighter --brand below, where that blue has plenty of contrast. */
  color: var(--lp-light-accent);
  font-weight: 600;
  text-decoration: none;
}

/* Underline borrowed from the navbar's active link (.cme-nav .nav-link.active
   ::after in base.css): a 2px rounded bar sitting 4px under the text, rather
   than a border on the box. Two benefits over the border it replaces — it
   tracks the text instead of the element, and it can be animated, so it wipes
   in from the left on hover/focus instead of appearing all at once.

   Scoped to a.lp-link deliberately: the "Also supported" disclosure is a
   <summary class="lp-link"> whose ::after already carries the +/- sign, and a
   second ::after rule would overwrite it. */
a.lp-link::after {
  content: "";
  display: block;
  height: 2px;
  margin-top: 4px;
  border-radius: 999px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--motion-fast) var(--ease-ui);
}
a.lp-link:focus-visible::after { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  a.lp-link::after { transition: none; }
}

.lp-dark-section .lp-link,
.lp-quote-band .lp-link { color: var(--brand); }
.lp-link--quiet { font-weight: 500; opacity: 0.85; margin-left: 20px; }

/* -----------------------------
   Hero follow-up: van video strip + Fully Mobile Service. Both are sized
   to their own content (the video by its fixed aspect-ratio, the text by
   its own text) rather than stretched to fill the viewport — the explicit
   background here is what keeps the strip reading as one continuous light
   block instead of exposing the page's dark background in any rounding
   gap between the two.
   ----------------------------- */
.lp-hero-followup {
  display: flex;
  flex-direction: column;
  background: var(--lp-light-bg);
  scroll-margin-top: 0;
}

/* -----------------------------
   Engineering capabilities
   ----------------------------- */
/* Fades from the site's light background colour into a light grey close
   to the video's own studio backdrop (rgb(210,210,210), sampled from the
   rendered video) — kept lighter than an exact match so the strip reads
   as white/light rather than a grey band, with just enough grey at the
   very bottom to ease into the video without a hard seam. Height matches
   the navbar so it reads as a deliberate continuation of it. */
.lp-capabilities__banner-pad {
  width: 100vw;
  left: 50%;
  margin-left: -50vw;
  position: relative;
  height: var(--nav-h, 90px);
  flex: 0 0 auto;
  background: linear-gradient(180deg, var(--lp-light-bg) 0%, rgb(233, 233, 233) 100%);
}
/* Outer slot: retained as the video/scrub target, but no longer used as a
   nav destination. It deliberately has no scroll-margin so any stale
   #van-video jump lands on the Mobile/video band itself instead of leaving a
   navbar-height slice of the hero visible above it. Kept separate from
   .lp-capabilities__banner (the video's own
   fixed-aspect-ratio box) because at ≥901px the slot stretches to match
   the full height of the #mobile-service text column beside it (see the
   media query below) so the video can be vertically centred within that
   space without moving the anchor's own top edge — if the video box
   itself were both the anchor AND the thing being centred/shortened,
   centring it would pull its top down below the text column's top,
   and jumping here would then leave the text heading uncovered by the
   scroll offset and hidden under the fixed nav (this happened before:
   the video and text were separately aligned in the same grid row, and
   whichever was shorter could drift above or below the other). */
.lp-capabilities__banner-slot {
  scroll-margin-top: 0;
}
.lp-capabilities__banner {
  position: relative;
  /* A plain full-width sibling between the hero and the next section (not
     nested in any padded .lp-section), so it sits flush against the bottom
     of the hero above with zero gap — no padding to cancel out. Breaks out
     to the full viewport width the same way a full-bleed hero would. */
  width: 100vw;
  left: 50%;
  margin-left: -50vw;
  /* Fixed to the source video's own 16:9 ratio (matches cme_van.mp4) at
     every width, so its rendered shape never changes as the page scales —
     only object-fit:cover's crop amount would shift with an arbitrary
     container ratio, and a variable-height container (the old flex:1
     approach) made the crop shift on every resize, which is what broke the
     fade overlays that used to mask its edges. Fixed ratio removed the
     need for those fades entirely. */
  aspect-ratio: 16 / 9;
  overflow: hidden;
}
.lp-capabilities__banner-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
/* Top/bottom fades sized as a % of the banner's own (now fixed-ratio) box,
   so they scale with it instead of drifting out of proportion at
   different widths the way a fixed px height would. */
.lp-capabilities__banner::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 12%;
  background: linear-gradient(to bottom, rgb(233, 233, 233), transparent);
  /* Painted above the video: as a position:auto-z-index sibling that comes
     first in the DOM, it would otherwise paint (and get hidden) behind the
     video, which comes after it. */
  z-index: 1;
  pointer-events: none;
}
.lp-capabilities__banner::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 12%;
  background: linear-gradient(to bottom, transparent, var(--lp-light-bg));
  pointer-events: none;
}

/* -----------------------------
   Fully mobile / nationwide coverage
   ----------------------------- */
/* Below ~900px wide, video-above/content-below (stacked) is the right
   call — see the min-width:901px override further down for the
   side-by-side desktop layout. Content sits right beneath the video,
   both sized to their own content rather than stretched to fill the
   viewport. */
#mobile-service.lp-section {
  flex: 0 0 auto;
  min-height: auto;
  justify-content: flex-start;
  /* Overlap onto the video (negative margin-top + transparent background)
     temporarily pulled out — diagnostic step to isolate whether the
     overlapping card is what's hiding the scroll-scrub motion, separate
     from whether the video is updating at all. Sits flush below the video
     again, same as before that treatment. */
  padding-top: clamp(24px, 4vw, 48px);
  padding-bottom: clamp(20px, 3vw, 40px);
}
.lp-mobile-service__lede {
  max-width: 68ch;
  margin: 12px 0 24px;
  line-height: 1.6;
  color: var(--lp-light-muted);
}
/* Full-width band beneath both the video and text columns, holding the
   six feature points as equal items in a grid — rather than being
   squeezed into just the text column. Two columns even at phone widths
   (three from 901px up) so the list reads as a compact block instead of
   a long single-file scroll. */
.lp-mobile-service__extras {
  padding-bottom: clamp(32px, 5vw, 64px);
}
.lp-mobile-service__extras .cme-section-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 18px 16px;
  align-items: center;
  /* The band already sits below #mobile-service's own bottom padding and
     that section's container padding, so .cme-section-container's default
     56px top (28px on mobile) was a third helping — ~144px between the CTA
     row and the first point. Trimmed here only; the bottom stays as-is. */
  padding-top: clamp(6px, 1.5vw, 22px);
}
/* Lets the <li>s (and the CTA <a> sibling) participate directly as items
   of the parent grid, instead of the <ul> being one grid cell of its own —
   keeps the list semantics without an extra nesting level to lay out. */
.lp-mobile-service__points {
  display: contents;
  list-style: none;
  margin: 0;
  padding: 0;
}
.lp-mobile-service__points li {
  display: flex;
  align-items: center;
  gap: 16px;
  font-weight: 800;
  font-size: clamp(1.05rem, 1.7vw, 1.2rem);
  color: var(--lp-light-ink);
}
.lp-mobile-service__points-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--brand) 14%, white);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 22%, transparent);
}
.lp-mobile-service__points i {
  color: var(--brand-dark, var(--brand));
  font-size: 1.15rem;
}
.lp-mobile-service__note {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 10px 0 0;
  font-size: 0.85rem;
  color: var(--lp-light-muted);
  text-align: center;
}
.lp-mobile-service__note i {
  color: var(--lp-light-muted);
  font-size: 0.9em;
}
.lp-mobile-service__actions {
  margin-top: clamp(24px, 4vw, 36px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
}

@media (max-width: 640px) {
  /* Matches the same full-width-primary-action pattern used at this
     breakpoint elsewhere (e.g. .lp-capability-story__actions .lp-btn) —
     the callout button reads as full-width on mobile rather than sized to
     its own text, with the plain arrow link stacked above it. */
  .lp-mobile-service__actions {
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
  }
  .lp-mobile-service__actions .lp-btn { width: 100%; }
  /* align-self, not text-align: the parent stretches its children, and
     a.lp-link draws its underline with an ::after bar sized to the element,
     so a stretched link gave a rule far wider than its label. Shrinking the
     link to its content keeps it centred and keeps the underline the width
     of the label. */
  .lp-mobile-service__actions .lp-link { align-self: center; }
}

/* Side-by-side layout once there's enough width to give the video and the
   text their own columns instead of stacking. Below this width the video
   was getting squeezed down to a thin strip against the fixed-ratio box's
   own width, so it reads better full-width above the text. */
@media (min-width: 901px) {
  .lp-hero-followup {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto auto;
    width: 100vw;
    position: relative;
    left: 50%;
    margin-left: -50vw;
    /* Every other area in the grid paints its own opaque background (the
       pad's gradient, mobile-service's lp-light-section), so this only
       ever shows through above/below the video now that it's centred at
       its own aspect ratio instead of stretched to fill the cell —
       without it that gap exposed the page's raw dark body background. */
    background: var(--lp-light-bg);
    /* The text column already owns its own top padding. Keeping another
       navbar-height spacer here doubled the visible gap above the Mobile
       heading on desktop; this leaves only a small breathing gap. */
    padding-top: 10px;
  }
  .lp-capabilities__banner-pad {
    /* The stacked layout uses this strip between the hero and van video. In
       the desktop split, the Mobile row starts immediately after the hero. */
    display: none;
  }
  .lp-capabilities__banner-slot {
    grid-column: 2;
    grid-row: 2;
    /* Stretch to the row's full height — i.e. match #mobile-service's
       height exactly, since that's the other item in this row and it's
       sized to its own content. This is what keeps the slot's (and so
       #van-video's) top edge level with the text column's top edge
       regardless of which one is taller at a given width, so the anchor
       scroll always clears the fixed nav for both. */
    align-self: stretch;
    /* The video itself keeps its fixed 16:9 box (see .lp-capabilities__
       banner below) rather than being stretched to fill this now-taller
       slot — flex centres that fixed-size box within the extra height
       instead, which is the actual "lower down, vertically centred"
       positioning being asked for. */
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .lp-capabilities__banner {
    width: 100%;
    left: auto;
    margin-left: 0;
    /* aspect-ratio and object-fit:cover are inherited from the base rule
       (same fixed 16:9 shape at every width) — stretching a landscape shot
       into a tall narrow column would otherwise force heavy horizontal
       cropping (the side-graphic text was getting clipped). Vertical
       position now comes from the parent slot's flex centring above,
       not from this element directly. */
    flex: 0 0 auto;
  }
  /* Side-by-side, the video floats on the light grid background rather
     than sitting flush against the pad's own backdrop-matched grey, so it
     needs an actual fade to the page's light colour (not the grey studio
     backdrop the base rule fades to) at both edges. */
  .lp-capabilities__banner::before {
    background: linear-gradient(to bottom, var(--lp-light-bg), transparent);
  }
  .lp-capabilities__banner::after {
    background: linear-gradient(to bottom, transparent, var(--lp-light-bg));
  }
  #mobile-service.lp-section {
    grid-column: 1;
    grid-row: 2;
    height: auto;
    /* Top-aligned, matching .lp-capabilities__banner-slot's stretch above
       — both sit flush with the row's top, so the video can be centred
       within its own (taller) slot without this heading drifting above
       the slot's top and under the fixed nav when #van-video is jumped to. */
    align-self: start;
    justify-content: center;
    padding: clamp(32px, 4vw, 64px);
    /* This section is one half of a full-viewport split row, so its own
       .cme-section-container centres inside a 50vw column rather than on the
       page — leaving the heading 22px in from the page edge at 1440px and
       51px at 1280px, while the points band directly beneath it (which spans
       the whole row) sat correctly. Half the column is half the page, so the
       page edge is reachable from here. */
    padding-left: max(var(--cme-section-gx), calc(100% - var(--cme-section-maxw) / 2));
    /* Shorter than the other sides — pulls the points row (the grid row
       below) up closer to this text/video row instead of leaving it at
       the same generous gap as the padding around the text itself. */
    padding-bottom: clamp(20px, 3vw, 32px);
    /* Side-by-side, the text sits beside the video, not stacked beneath
       it — the base rule's negative margin-top (pulling the text up to
       overlap the video below it) doesn't apply here. */
    margin-top: 0;
  }
  /* Own row beneath both the video and text columns, spanning their full
     combined width — the five points plus the CTA laid out as one row
     that wraps into two, rather than living inside just the text column. */
  .lp-mobile-service__extras {
    grid-column: 1 / -1;
    grid-row: 3;
  }
  .lp-mobile-service__extras .cme-section-container {
    grid-template-columns: repeat(3, 1fr);
  }
  /* Neutralised: the section now owns the left offset, so this container
     would otherwise re-centre its own measure inside the already-offset
     column and drift back off the page edge. */
  #mobile-service.lp-section > .cme-section-container {
    max-width: none;
    margin-inline: 0;
    padding-inline: 0;
  }
}

/* -----------------------------
   Engineering capabilities — full-bleed video carousel
   ----------------------------- */
/* The carousel is now the section's only content and is full-bleed video,
   so it should sit flush against the section's edges — .lp-section's own
   padding would otherwise leave a band of the light section background
   showing above/below the video. The carousel's own __content padding
   handles spacing for the heading/tabs/text that sit on top of it. */
#capabilities.lp-section {
  padding-block: 0;
  /* The carousel used to just take its content's height (~78vh) inside
     this min-height:100vh section, which — combined with centering — left
     a band of the section's own light background showing above/below it.
     Growing it to fill the section instead removes that gap. Kept at the
     full 100vh (inherited from .lp-section, not capped) so the video is
     genuinely full-bleed — the panels being short now is instead handled
     by bottom-anchoring their content (see .lp-capability-carousel__content
     justify-content below) rather than shrinking the section around them. */
  align-items: stretch;
  /* Unlike #van-video (which has a dedicated nav-height pad strip to land
     in), this section is full-bleed video top-to-bottom like the hero —
     it should scroll flush with no scroll-margin so the video runs up
     behind the fixed, transparent navbar instead of stopping short and
     exposing a gap of the section's own background above it. */
}
.lp-capability-carousel {
  position: relative;
  width: 100vw;
  left: 50%;
  margin-left: -50vw;
  overflow: hidden;
  color: var(--lp-dark-ink);
  /* Fills the section's full min-height:100vh (inherited from .lp-section
     via align-items:stretch above) rather than sizing to its own content —
     this is what the video background actually covers, so it needs to be
     the full screen. */
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 100vh;
  min-height: 100svh;

  /* Single spacing scale for the whole content block (eyebrow, tabs,
     bullet grid, actions) — every gap below reads off one of these
     instead of separate hardcoded numbers, so the rhythm between layers
     stays proportional as the viewport changes. Redefined wholesale (not
     per-property) by the narrow and short-viewport queries further down,
     rather than each consumer having its own override. */
  --cap-gap-block: clamp(28px, 3.4vw, 44px);        /* tabs→title */
  --cap-gap-list-actions: clamp(28px, 2.6vw, 40px); /* title→bullets, bullets→actions */
  --cap-gap-row: clamp(18px, 1.6vw, 24px);          /* bullet grid row gap */
  --cap-gap-col: clamp(24px, 2.4vw, 32px);          /* bullet grid column gap */
  --cap-pad-block: var(--lp-jump-pad);              /* content's own top/bottom safe space — shared with Sectors/Storage/About, see .lp-page */
}

.lp-capability-carousel__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.lp-capability-bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* Media dissolve between clips. Footage, not a UI state change — it gets
     the hero duration so the landing hero, this carousel and the
     service-detail hero all dissolve at the same rate. */
  transition: opacity var(--motion-hero) var(--ease-ui);
}
.lp-capability-bg-video.is-active { opacity: 1; }
.lp-capability-carousel__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6, 16, 34, 0.55) 0%, rgba(6, 16, 34, 0.74) 55%, rgba(6, 16, 34, 0.88) 100%);
}

.lp-capability-carousel__content {
  position: relative;
  z-index: 1;
  /* Fills the carousel (now a flex column itself), so the eyebrow/heading/
     tabs/panels can be positioned anywhere within the full viewport height
     instead of a capped ~78vh box floating inside it. */
  flex: 1 1 auto;
  width: 100%;
  max-width: calc(var(--cme-section-maxw) + (var(--cme-section-gx) * 2));
  margin-inline: auto;
  padding-inline: var(--cme-section-gx);
  /* The video is allowed to run up behind the fixed, transparent navbar
     (see the "no scroll-margin" note on #capabilities.lp-section below),
     but the topbar text sitting at the true top of this flex column needs
     real breathing room below the nav, not just to graze its bottom edge —
     nav-h alone puts the topbar flush against the nav with zero visible
     gap, so this adds var(--cap-pad-block) on top of it rather than just
     taking whichever of the two is larger. */
  padding-top: calc(var(--nav-h, 90px) + var(--cap-pad-block));
  padding-bottom: var(--cap-pad-block);
  display: flex;
  flex-direction: column;
}

/* Thin divider row pinned to the true top of the section — the tab/panel
   block below is what's bottom-anchored (via margin-top:auto on
   .lp-capability-block), not this. Kept separate from the rest of the
   content's gap scale since it's a fixed top landmark, not part of the
   tabs→title→bullets rhythm. */
.lp-capability-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.lp-capability-topbar__hint {
  margin: 0;
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(244, 247, 251, 0.5);
}
@media (max-width: 520px) {
  .lp-capability-topbar__hint { display: none; }
}

/* Overrides .lp-light-section's dark-on-light eyebrow colour — it now sits
   on the video + scrim, not the section's light background. margin:0 kills
   Bootstrap reboot's default <p> margin-bottom (1rem), which would
   otherwise stack on top of .lp-capability-topbar's own flex alignment. */
.lp-capability-carousel .cme-eyebrow { color: var(--brand); margin: 0; }
.lp-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Tabs on top, then title/bullets/actions stacked below — a single
   left-aligned column pushed to the bottom of the content box (mirrors
   .lp-capability-topbar sitting fixed at the top), capped at roughly half
   the viewport width so the video keeps the other half uncovered. */
.lp-capability-block {
  display: flex;
  flex-direction: column;
  gap: var(--cap-gap-block);
  width: 100%;
  max-width: 640px;
  margin-top: auto;
}

/* Stacked column above the title — full-width rows with their own
   bottom-border track, so it reads as a compact vertical menu rather than
   spreading across the whole column's width. */
.lp-capability-tabs {
  display: flex;
  flex-direction: column;
}
.lp-capability-tab {
  position: relative;
  appearance: none;
  background: none;
  border: none;
  cursor: pointer;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: clamp(14px, 1.8vw, 20px) 0;
  font: inherit;
  color: rgba(244, 247, 251, 0.55);
  transition: color var(--motion-fast) var(--ease-ui);
}

.lp-capability-tab:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 4px;
  border-radius: 2px;
}
.lp-capability-tab.is-active { color: #f4f7fb; }
.lp-capability-tab__text {
  display: inline-flex;
  align-items: baseline;
  gap: 14px;
}
.lp-capability-tab__index {
  font-size: 0.85rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  opacity: 0.6;
}
.lp-capability-tab.is-active .lp-capability-tab__index {
  opacity: 1;
  color: var(--brand);
}
.lp-capability-tab__label {
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.2;
}
/* Arrow reveals on the active row and on hover — a quiet affordance that
   this list is clickable, rather than restyling the row itself. */
.lp-capability-tab__arrow {
  font-size: 0.95rem;
  color: var(--brand);
  opacity: 0;
  transition: opacity var(--motion-fast) var(--ease-ui);
  flex: 0 0 auto;
}
.lp-capability-tab.is-active .lp-capability-tab__arrow { opacity: 1; }

/* Static track under every row, so the list always reads as a set of equal
   items — the coloured fill (below) is drawn on top, per-tab, so the
   countdown always lines up with the row it actually belongs to. */
.lp-capability-tab::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(244, 247, 251, 0.2);
}
.lp-capability-tab.is-active::after { background: rgba(244, 247, 251, 0.4); }
/* Scaled, not widened. This bar runs for the full auto-advance interval
   whenever the carousel is on screen, which made it comfortably the most
   animated thing on the page — and animating width means a layout pass every
   frame. scaleX from a left origin is the same picture off the compositor.
   The driver in landing_preview.html sets the transform; see runProgress(). */
.lp-capability-tab__progress-bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: var(--brand);
  z-index: 1;
}

/* All panels share the same grid cell so the incoming/outgoing pair can be
   visible at once during a transition (see setPanelMotion() in the page
   script) — the row sizes to the tallest of whichever panels are currently
   un-hidden, so it grows/shrinks smoothly rather than snapping. */
.lp-capability-panels {
  position: relative;
  display: grid;
  overflow: hidden;
}
.lp-capability-panel {
  grid-area: 1 / 1;
  position: relative;
  z-index: 1;
  /* Heading→list and list→actions gaps read off the same scale used
     elsewhere in the block (was a hardcoded margin-bottom on the list,
     disconnected from the rest of the rhythm and inert on short/narrow
     viewports). */
  display: flex;
  flex-direction: column;
  gap: var(--cap-gap-list-actions);
}
.lp-capability-panel[hidden] { display: none; }
/* Per-category headline — the tab list no longer doubles as the section's
   heading (it's a consistent-size nav list now), so this carries that
   role instead, sitting beside it in the panel column. */
.lp-capability-panel__heading {
  margin: 0;
  font-size: clamp(1.6rem, 2.6vw, 2.4rem);
  font-weight: 700;
  line-height: 1.15;
}

.lp-capability-panel--enter-right,
.lp-capability-panel--enter-left {
  z-index: 2;
}
/* Pure positional slide, no fade — panels swap by sliding fully off/on
   their own width rather than crossfading in place. */
.lp-capability-panel--enter-right { animation: lp-capability-panel-enter-right var(--motion-hero) var(--ease-premium) both; }
.lp-capability-panel--enter-left { animation: lp-capability-panel-enter-left var(--motion-hero) var(--ease-premium) both; }
.lp-capability-panel--exit-left { animation: lp-capability-panel-exit-left var(--motion-ui) var(--ease-ui) both; }
.lp-capability-panel--exit-right { animation: lp-capability-panel-exit-right var(--motion-ui) var(--ease-ui) both; }

@keyframes lp-capability-panel-enter-right {
  from { transform: translateX(100%); }
  to { transform: none; }
}
@keyframes lp-capability-panel-enter-left {
  from { transform: translateX(-100%); }
  to { transform: none; }
}
@keyframes lp-capability-panel-exit-left {
  from { transform: none; }
  to { transform: translateX(-100%); }
}
@keyframes lp-capability-panel-exit-right {
  from { transform: none; }
  to { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .lp-capability-panel--enter-right,
  .lp-capability-panel--enter-left,
  .lp-capability-panel--exit-left,
  .lp-capability-panel--exit-right {
    animation: none;
  }
}

/* Full-width row across the section on desktop — the panel no longer
   carries a reading-width lede/desc, so the bullet list is the panel's
   whole body and can span the full carousel width instead of being
   capped to a narrow reading column. Fixed column counts (rather than
   auto-fit) so 6 items always land as a clean, evenly-filled block at
   every tier — auto-fit's column count depends on viewport width, which
   could strand the 6th item alone on its own row. 3 columns down to
   1000px (comfortable even for the longest labels), 2 columns from
   641–999px, 1 column at/below 640px (see media queries below). */
.lp-capability-story__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--cap-gap-row) var(--cap-gap-col);
}
.lp-capability-story__list li {
  padding-left: 22px;
  position: relative;
  font-size: 0.92rem;
  line-height: 1.5;
  color: #f4f7fb;
}
.lp-capability-story__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 3px rgba(2, 132, 199, 0.25);
}
/* Same reason as .lp-support-card__link: this row stretches its children on
   narrow widths, so the arrow link's underline outran its text. */
.lp-capability-story__actions .lp-link {
  align-self: flex-start;
  width: fit-content;
}

.lp-capability-story__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
}
.lp-capability-carousel .lp-link { color: var(--brand); font-weight: 600; }

@media (max-width: 900px) {
  .lp-capability-carousel__content { min-height: min(88vh, 780px); }
}

/* Short laptop windows (1366×768, 1440×800, 1280×720, …) — clamp()'s vw
   unit tracks width, not height, so a wide-but-short window would
   otherwise still get "large desktop" spacing and risk crowding the
   bottom edge. Redefines the same scale used everywhere else at a
   tighter setting rather than shrinking type or introducing new rules
   elsewhere; min-width keeps this from also firing on tall phones. */
@media (min-width: 900px) and (max-height: 800px) {
  .lp-capability-carousel {
    --cap-gap-block: clamp(16px, 2vw, 24px);
    --cap-gap-list-actions: clamp(16px, 1.6vw, 22px);
    --cap-gap-row: clamp(12px, 1.2vw, 16px);
    /* --cap-pad-block not redefined here — it already reads off the
       shared --lp-jump-pad (.lp-page), which has its own matching
       short-viewport override. */
  }
}

@media (max-width: 640px) {
  /* Mobile: one column × 6 rows, with its own (tighter) row-gap target —
     redefined on the shared scale so .lp-capability-story__list doesn't
     need its own separate override. */
  .lp-capability-carousel { --cap-gap-row: clamp(12px, 3vw, 18px); }
  .lp-capability-story__list { grid-template-columns: 1fr; }

  /* Side-by-side "Explore -> / Get a quote" wraps awkwardly at narrow
     widths — stack them instead, with the quote button reading as a full-
     width primary action rather than a squeezed inline pair. */
  .lp-capability-story__actions {
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
  }
  .lp-capability-story__actions .lp-btn { width: 100%; }
  .lp-capability-carousel .lp-capability-story__actions .lp-link { align-self: center; }
}

/* -----------------------------
   Editorial head — shared by Sectors, Our Work and Feedback. A thin top
   rule with a mono, numbered-spec-sheet eyebrow in a narrow left column
   next to the heading; body copy below is indented to the same column
   so the whole section reads as one deliberate grid instead of three
   independently-designed blocks.
   ----------------------------- */
.lp-editorial-head {
  display: grid;
  grid-template-columns: minmax(140px, 22%) 1fr;
  gap: 10px 32px;
  align-items: start;
  padding-top: 22px;
  border-top: 1px solid var(--lp-dark-border);
}
.lp-light-section .lp-editorial-head { border-top-color: var(--lp-light-border); }
/* Sectors is the first content section after the hero/mobile-service
   block — the top rule reads as a stray line right under that band
   rather than a divider between two sections, so it's dropped here only
   (other .lp-editorial-head sections keep it). */
#sectors .lp-editorial-head {
  border-top: none;
  /* .cme-section-container's gutter (56px) + this component's own 22px
     padding-top add up to 78px on top of the navbar clearance — pulled
     up further than just matching Capabilities' own position, for a
     tighter gap under the nav on this section specifically. */
  margin-top: -30px;
}
#feedback .lp-editorial-head { border-top: none; }
.lp-editorial-head__eyebrow {
  margin: 0;
  padding-top: 6px;
  /* Matches .cme-eyebrow rather than being a second uppercase label. Three
     sections used a light DM Mono eyebrow and three used the bold sans one
     for exactly the same job — naming the section — which read as two
     systems on one page. The mono face is kept for genuine meta (reviewer
     names, footer column headings), where it is doing a different job. */
  font-size: 0.78rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--brand);
}
.lp-light-section .lp-editorial-head__eyebrow { color: var(--brand-dark); }
.lp-editorial-head .cme-section-title { margin: 0; }
@media (max-width: 760px) {
  .lp-editorial-head { grid-template-columns: 1fr; gap: 8px; }
}
.lp-editorial-indent { margin-top: 4px; }
@media (min-width: 760px) {
  /* The 22% matches .lp-editorial-head's first column, but that grid also
     has a 32px column gap — without it the heading sat 32px right of the
     copy it introduces. */
  .lp-editorial-indent { margin-left: calc(22% + 32px); max-width: 62ch; }
}

/* -----------------------------
   Sectors — ruled lists
   ----------------------------- */
/* #sectors's own scroll-margin-top (below) reserves a nav-height gap when
   jump-navigating here, but that gap shows whatever sits immediately
   above #sectors in normal flow — the capabilities carousel's video —
   not anything belonging to Sectors itself. Landing here then briefly
   shows a stray fragment of the carousel (its tab glow/UI) floating
   behind the transparent fixed nav. Same fix as .lp-capabilities__banner-pad
   for #van-video: a dedicated, correctly-coloured spacer sized to the nav
   height, inserted right before the anchor, so the reserved gap always
   shows Sectors' own dark background instead of the previous section.

   Generalised into .lp-jump-pad (+ a light/dark colour modifier) rather
   than a Sectors-only class, since the same fix is needed anywhere a jump
   target's own background colour differs from whatever section precedes
   it — reused below for #support and #about too. */
/* -----------------------------
   Support band video
   -----------------------------
   Full-bleed sibling, same escape as .lp-jump-pad and the van banner: a
   plain div outside any padded section, widened to the viewport so it butts
   against the dark band above with nothing to cancel out.

   Fixed aspect ratio rather than a height, so the shape — and therefore how
   much object-fit:cover crops — is the same at every width. A variable-height
   container is what made the van banner's crop drift on resize. Wider than
   16:9 on desktop: at 1440px a 16:9 band is 810px tall, which is not a
   transition between two sections, it is a third section.

   No edge fades. The van banner gradients into its neighbours because it is
   a light band between two light sections and a visible seam there would
   read as a mistake. This one divides navy from white, so the seam is the
   point — a fade just makes a crisp intended edge look like a soft
   unintended one. */
.lp-support-video {
  position: relative;
  width: 100vw;
  left: 50%;
  margin-left: -50vw;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  display: grid;
  align-items: end;
  /* Painted behind the clips so the band is never a white gap while the
     poster is still being fetched. */
  background: var(--lp-dark-bg);
}

@media (min-width: 901px) {
  .lp-support-video { aspect-ratio: 21 / 9; }
}

.lp-support-video__media {
  position: absolute;
  inset: 0;
}

/* Crossfaded in pairs, same contract as .cme-hero__video: both stacked, the
   active one at opacity 1. --motion-fast (180ms) rather than the hero's
   hard-coded 0.2s — near enough to look the same, and this stylesheet is one
   of the two the motion-token test polices. A long dissolve between two
   clips of the same subject reads as a focus problem anyway. */
.lp-support-video__clip {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: 0;
  transition: opacity var(--motion-fast) var(--ease-ui);
}
.lp-support-video__clip.is-active { opacity: 1; }

/* Left-weighted, matching .cme-cat__video-overlay on the detail pages: the
   statement sits bottom-left, so that is where the ground has to be darkest.
   Deliberately lighter at the bottom edge than the detail-page version,
   which closes at 0.92 — here that would put a band of near-navy directly
   above the white section and undo the hard edge. */
.lp-support-video__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(6, 16, 34, 0.82) 0%, rgba(6, 16, 34, 0.42) 45%, rgba(6, 16, 34, 0.12) 100%),
    linear-gradient(180deg, rgba(6, 16, 34, 0.34) 0%, rgba(6, 16, 34, 0.10) 40%, rgba(6, 16, 34, 0.48) 100%);
  pointer-events: none;
}

/* Grid rather than flow: the statements stack in one cell so they crossfade
   in place, and the cell takes the height of the tallest of them — so the
   band does not resize as they swap. */
.lp-support-video__content {
  position: relative;
  z-index: 1;
  display: grid;
  width: 100%;
  padding-block: clamp(28px, 4vw, 56px);
}

/* Detail-page hero proportions (.lp-svc-hero__title), not a section title:
   same clamp and the same short measure, so a reader arriving here from a
   service page recognises the register. */
.lp-support-video__statement {
  grid-area: 1 / 1;
  /* Transition, not an animation: this fires on every swap for as long as
     the band is on screen, and the reveal keyframes are one-shot entrances.
     The duration and easing are the existing title-role tokens, so the words
     arrive with the same weight here as they do anywhere else on the page —
     no new timing values. */
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--motion-in-title) var(--ease-premium),
    transform var(--motion-in-title) var(--ease-premium);
  margin: 0;
  max-width: 20ch;
  font-size: clamp(1.9rem, 4.4vw, 3.1rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: #fff;
  /* The scrim does the work; this only covers the case where a bright frame
     lands under the first line mid-clip. */
  text-shadow: 0 1px 24px rgba(6, 16, 34, 0.45);
}

.lp-support-video__statement.is-active {
  opacity: 1;
  transform: translateY(0);
}

/* The rotation never starts under reduced motion, so the first statement is
   the only one ever shown — it must not be left at opacity 0 waiting for a
   transition that will not run. */
@media (prefers-reduced-motion: reduce) {
  .lp-support-video__statement {
    transition: none;
    transform: none;
  }
}

@media (max-width: 640px) {
  /* Taller box. 16:9 at 390px is a 219px band, and the statement needs three
     lines inside it — the picture ends up as a stripe behind text rather
     than a band with text on it. */
  .lp-support-video { aspect-ratio: 4 / 3; }

  /* The desktop scrim is left-weighted because the statement occupies the
     left third there. At this width it spans the full column and runs out
     past the dark end of that gradient: measured 3.01:1 on the marina clip,
     the AA large-text threshold exactly, which is no margin at all on a
     moving image whose next frame may be brighter.
     A near-flat floor instead, since there is no light side left to protect. */
  .lp-support-video__scrim {
    background:
      linear-gradient(180deg,
        rgba(6, 16, 34, 0.52) 0%,
        rgba(6, 16, 34, 0.58) 40%,
        rgba(6, 16, 34, 0.84) 100%);
  }

  /* Smaller and wider than the desktop measure: at 15ch and 1.9rem this ran
     to four lines, which is a paragraph, not a statement. */
  .lp-support-video__statement {
    max-width: 18ch;
    font-size: clamp(1.5rem, 6.2vw, 2rem);
  }
}

.lp-jump-pad {
  width: 100vw;
  left: 50%;
  margin-left: -50vw;
  position: relative;
  height: var(--nav-h, 90px);
}
.lp-jump-pad--dark { background: var(--lp-dark-bg); }
.lp-jump-pad--light { background: var(--lp-light-bg); }

/* This section's content (heading + copy + three short columns) is shorter
   than a full viewport, so the base .lp-section min-height:100vh — combined
   with vertical centering — left an oversized band of empty navy above and
   below it on typical laptop/desktop heights. Capped the same way as the
   capabilities carousel: falls back to the viewport height itself whenever
   that's already the smaller value (short/mobile screens). */
#sectors.lp-section {
  min-height: min(100vh, 760px);
  min-height: min(100svh, 760px);
  /* Vertical centering inside .lp-section makes the gap above the heading
     vary with content/viewport height — jump-navigating here would
     otherwise sometimes leave the heading tucked behind the fixed navbar.
     A fixed scroll-margin guarantees the section top (and so the heading)
     always clears the navbar, matching .lp-capabilities__banner below. */
  scroll-margin-top: var(--nav-h, 90px);
  /* Pinned to the top instead of the base .lp-section's vertical centring
     (which also contributes its own clamp(48px,7vw,96px) top padding —
     overridden away here) so "Sectors We Cover" lands at the same on-
     screen position "Engineering Capabilities" sits at on the services
     section, rather than floating mid-section. No extra offset is added
     beyond that: scroll-margin-top above already clears the navbar, and
     .cme-section-container's own vertical gutter plus .lp-editorial-head's
     22px top padding already supply the same breathing room #capabilities
     gets from --cap-pad-block — adding a third offset on top of those
     would push the eyebrow back down past the target position. */
  justify-content: flex-start;
  padding-top: 0;
}
.lp-sectors__lede { max-width: 62ch; margin: 0; }
.lp-sectors {
  margin-top: 36px;
  border-top: 1px solid var(--lp-dark-border);
  display: grid;
  grid-template-columns: 1fr;
}
@media (min-width: 720px) {
  .lp-sectors {
    grid-template-columns: repeat(3, 1fr);
    /* Title row + 6 item rows. Each .lp-sector-group subgrids this same
       row-track set, so a row's height is driven by the tallest item in
       that row across all three columns — keeping icons/text aligned even
       when one column's description wraps to two lines. */
    grid-template-rows: auto repeat(6, auto);
    row-gap: 16px;
  }
}
.lp-sector-group {
  padding: 26px 0;
  border-bottom: 1px solid var(--lp-dark-border);
}
@media (min-width: 720px) {
  .lp-sector-group {
    display: grid;
    grid-row: 1 / span 7;
    grid-template-rows: subgrid;
    row-gap: inherit;
    padding: 26px clamp(16px, 2.5vw, 28px);
    border-bottom: none;
  }
  .lp-sector-group:not(:first-child) { border-left: 1px solid var(--lp-dark-border); }
  .lp-sector-group:first-child { padding-left: 0; }
  .lp-sector-group:last-child { padding-right: 0; }
}
.lp-sector-group__title {
  font-size: 0.95rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--brand);
  margin: 0 0 14px;
}
@media (min-width: 720px) {
  /* Title's own row in the subgrid; row-gap above already spaces it
     from the first item, so the old margin-bottom would double up. */
  .lp-sector-group__title { margin-bottom: 0; }
}
.lp-icon-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
@media (min-width: 720px) {
  /* Unwrap the <ul> so each <li> becomes a direct grid item of
     .lp-sector-group and lands on its own subgrid row. */
  .lp-icon-list { display: contents; }
}
.lp-icon-list li {
  display: flex;
  gap: 12px;
}
@media (min-width: 720px) {
  .lp-icon-list li { align-items: flex-start; }
}
.lp-icon-list__icon {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 0.85rem;
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 16%, transparent);
}
.lp-icon-list__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.lp-icon-list__body strong { font-size: 0.92rem; font-weight: 700; }
.lp-icon-list__body span {
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--lp-dark-muted);
}

/* -----------------------------
   Support — cross-sell / discovery area for secondary, recurring services
   (storage, service contracts, maintenance plans, seasonal care).
   ----------------------------- */
#support.lp-section,
#about.lp-section {
  /* Same reasoning as #sectors above: vertical centering means the gap
     above the heading isn't reliable on its own, so pin a floor equal to
     the fixed navbar's height for jump-navigation from the nav links. */
  scroll-margin-top: var(--nav-h, 90px);
  justify-content: flex-start;
}
/* #support uses .lp-editorial-head (like #sectors), which already
   contributes its own 22px top padding on top of .cme-section-container's
   vertical gutter — together those already land the eyebrow at
   essentially the same position --lp-jump-pad targets elsewhere, so no
   extra section-level padding-top is added on top of them here either. */
#support.lp-section { padding-top: 0; }
@media (min-width: 760px) {
  #support.lp-section {
    scroll-margin-top: calc(var(--nav-h, 90px) - 14px);
  }
}
/* Dropped for the same reason as #sectors above — the rule read as a
   stray line rather than a section divider. Removing it also removes
   the extra 1px it added to the stack, so the correction below matches
   #sectors' original (pre-tightening) -6px rather than the old -8px.
   #support also recomposes the shared eyebrow/heading pattern into one
   stacked editorial block instead of #sectors' two-column split — with
   only one section using this component, the eyebrow no longer reads as
   stranded in the left gutter while the heading/intro start well to the
   right of it. */
#support .lp-editorial-head {
  border-top: none;
  margin-top: -6px;
  display: block;
}
#support .lp-editorial-head__eyebrow { margin-bottom: 18px; }
/* text-wrap: balance alone so narrow viewports pick their own natural,
   balanced line count instead of inheriting a desktop-tuned break. */
#support .lp-editorial-head .cme-section-title { text-wrap: balance; }
@media (min-width: 760px) {
  /* ch here is relative to the heading's own (much larger) font-size,
     not the container's — sized to force the "Support beyond our core /
     engineering services" break balance settles on once the column is
     wide enough that it would otherwise sit on a single line. */
  #support .lp-editorial-head .cme-section-title { max-width: 20ch; }
}
#support .lp-editorial-indent {
  margin-left: 0;
  margin-top: 18px;
  max-width: 50ch;
}
/* #about has no .lp-editorial-head wrapper, but it does have its own
   logo image (.lp-about__logo, 104px + 20px margin at desktop) directly
   above the eyebrow — real, visible content the other three sections
   don't have, not empty space. Adding the full shared gap on top of that
   would push its eyebrow noticeably lower than the others for no good
   reason, so — same as #sectors/#support — it relies on
   .cme-section-container's own vertical gutter alone; the logo above the
   eyebrow accounts for the remaining, legitimate difference in height. */
#about.lp-section { padding-top: 0; }

/* Four offer panels — 1 column on mobile, 2 columns from 700px up, then
   one row on wide desktop. Styled as quiet editorial
   panels (hairline border, faint surface lift, no shadow) rather than
   bordered/shadowed "SaaS" cards — deliberately closer to a spec sheet
   than a product tile. */
.lp-support-grid {
  margin-top: 44px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
@media (min-width: 700px) {
  .lp-support-grid { grid-template-columns: repeat(2, 1fr); gap: 24px 28px; }
}
@media (min-width: 1180px) {
  .lp-support-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 20px;
  }
}
.lp-support-card {
  display: flex;
  flex-direction: column;
  padding: clamp(26px, 3vw, 36px);
  background: color-mix(in srgb, #fff 60%, var(--lp-light-bg));
  border: 1px solid color-mix(in srgb, var(--lp-light-ink) 8%, transparent);
  border-radius: 6px;
}
/* The one genuinely capacity-limited offer gets a faint tint and a
   slightly more present edge — enough to notice, not enough to read as
   a promotional highlight. Targets the card via the label it contains
   rather than a template-level modifier class. */
.lp-support-card:has(.lp-support-card__label--availability) {
  background: color-mix(in srgb, var(--brand) 6%, color-mix(in srgb, #fff 60%, var(--lp-light-bg)));
  border-color: color-mix(in srgb, var(--brand-dark) 22%, transparent);
}
/* Adaptive brand lockup.
   ------------------------------------------------------------------
   Two wordmark variants occupy one box and crossfade as the glass navbar
   passes between dark video/navy bands and near-white sections; a phone
   breakpoint swaps both for the standalone emblem. The context comes from
   nav-context.js as [data-nav-context] on .cme-nav — the same attribute
   nav links can key off, so brand and navigation move together.

   Geometry notes:
   - The dark variant is in flow and defines the box. The light variant is
     absolutely pinned to it, so the two are pixel-identical and the
     crossfade cannot shift layout.
   - Width is fixed rather than height because the brief's target is a
     width (205-230px); 215px sits mid-range and clears the widest nav
     sheet (744px) inside the narrowest desktop container (992px) with
     ~33px to spare.
   - max-height guards the 90px bar against a taller-than-expected export,
     and object-fit: contain means that guard scales the artwork instead of
     squashing it. */
.cme-nav .navbar-brand .cme-brand {
  position: relative;
  display: block;
  line-height: 0;
}
.cme-nav .navbar-brand .cme-brand__mark {
  display: block;
  width: 215px;
  height: auto;
  max-height: 62px;
  object-fit: contain;
  object-position: left center;
  transition: opacity var(--motion-ui) var(--ease-ui);
}
.cme-nav .navbar-brand .cme-brand__mark--light {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.cme-nav[data-nav-context="light"] .cme-brand__mark--dark { opacity: 0; }
.cme-nav[data-nav-context="light"] .cme-brand__mark--light { opacity: 1; }

/* The wordmark runs at every width — below the desktop breakpoint the nav
   sheet collapses into the toggler, which frees more room for the brand, not
   less. It steps down twice so it keeps clear space beside the 56px toggler
   rather than filling the bar: at 320px the full 215px would leave only
   ~25px of slack. The emblem stays in the markup as the floor for very
   narrow viewports. */
/* Phones keep close to the full desktop size — below the desktop breakpoint
   the nav sheet collapses into the toggler, so the brand has *more* room, not
   less, and the earlier 180px step was shrinking it for no reason. Only the
   narrowest screens step down, and only enough to keep clear space beside the
   56px toggler. */
@media (max-width: 599.98px) {
  .cme-nav .navbar-brand .cme-brand__mark { width: 204px; }
}
@media (max-width: 359.98px) {
  .cme-nav .navbar-brand .cme-brand__mark { width: 186px; }
}


@media (prefers-reduced-motion: reduce) {
  .cme-nav .navbar-brand .cme-brand__mark { transition: none; }
}

/* Spot illustration marking each card's subject. The source PNGs are
   1024x1024 with the subject sitting in a centred horizontal band
   (roughly 29%-70% of the canvas height), so the box is cropped with
   object-fit: cover rather than contain — contain would letterbox the
   transparent margin and open an inconsistent gap above each title. If a
   replacement icon is composed differently, check it still fits the crop.

   The subjects are NOT drawn at a consistent scale within that shared
   canvas, and cover scales by width — so an identical box makes a long,
   slender hull (motor yacht, jet ski) read visibly smaller than a tall
   one. Widths below are tuned per icon to equalise optical area, not to
   equalise the box. */
.lp-support-card__avatar {
  display: block;
  width: 100%;
  max-width: 276px;
  height: 124px;
  margin: 0 auto 24px;
  object-fit: cover;
  object-position: center;
}
/* The workboat is drawn ~30% larger inside its canvas than the other
   three, so it needs a smaller box to land at the same optical size. */
.lp-support-card__avatar--large-subject { max-width: 240px; }

@media (min-width: 1180px) {
  .lp-support-card { padding: clamp(22px, 2vw, 28px); }
  .lp-support-card__avatar {
    height: 108px;
    margin-bottom: 20px;
  }
  .lp-support-card__title { font-size: 1.08rem; }
  .lp-support-card__desc {
    margin-bottom: 18px;
    font-size: 0.9rem;
    line-height: 1.55;
  }
  .lp-support-card__points {
    gap: 8px;
    margin-bottom: 22px;
  }
}

.lp-support-card__label {
  margin: 0 0 12px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}
/* Only Jet Ski Storage carries this — the one offer that's genuinely
   capacity-limited. Everything else stays label-free rather than
   borrowing the same urgency treatment for services with no real scarcity. */
.lp-support-card__label--availability { color: var(--lp-light-accent); }
.lp-support-card__title {
  margin: 0 0 12px;
  font-size: 1.22rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.25;
}
.lp-support-card__desc {
  margin: 0 0 22px;
  color: var(--lp-light-muted);
  font-size: 0.94rem;
  line-height: 1.65;
}
.lp-support-card__points {
  list-style: none;
  margin: 0 0 26px;
  padding: 0;
  display: grid;
  gap: 10px;
}
.lp-support-card__points li {
  padding-left: 16px;
  position: relative;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--lp-light-muted);
}
/* A hairline dash rather than a filled dot — quieter, and reads closer
   to a spec-sheet mark than a bullet-point UI list. */
.lp-support-card__points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 8px;
  height: 1px;
  background: color-mix(in srgb, var(--lp-light-ink) 38%, transparent);
}
/* Pushes the CTA (and the cross-link under it) to the bottom of the card
   regardless of how much description/point text precedes it, so all four
   buttons line up across the grid instead of trailing off at different
   heights. */
.lp-support-card__actions { margin-top: auto; }
/* Darker, flatter navy fill instead of the site's bright cyan gradient
   primary — repeated four times in one grid, the usual CTA gradient read
   as four identical bright rectangles. Scoped to this grid only; the
   gradient primary stays as-is everywhere else it's used. */
/* This grid's primary is a dark navy fill, not the bright gradient, so it
   keeps the white label — the deep navy ink used elsewhere would be 1.19:1
   here. Contrast is 18.37:1. */
.lp-support-card .lp-btn--primary { color: #fff; }

.lp-support-card .lp-btn--primary {
  background: var(--lp-light-ink);
  box-shadow: none;
}

.lp-support-card__link {
  display: inline-block;
  /* The card is a flex column, so inline-block alone still stretches this to
     the full card width — and .lp-link draws its underline with
     border-bottom, which follows the box rather than the text. Size it to
     its own label. */
  align-self: flex-start;
  width: fit-content;
  margin-top: 14px;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--lp-light-muted);
}


/* Closing fallback CTA — deliberately quieter than the four cards above:
   smaller heading, centred, no button (plain links only). */
.lp-support-fallback {
  margin-top: 48px;
  padding-top: 32px;
  border-top: 1px solid var(--lp-light-border);
  text-align: center;
}
.lp-support-fallback__title {
  margin: 0 0 8px;
  font-size: 1.05rem;
  font-weight: 700;
}
.lp-support-fallback__desc {
  margin: 0 0 16px;
  color: var(--lp-light-muted);
  font-size: 0.92rem;
}
.lp-support-fallback__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 16px;
}
.lp-support-fallback__or {
  color: color-mix(in srgb, var(--lp-light-muted) 82%, transparent);
  font-size: 0.86rem;
}
.lp-support-fallback__phone {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--lp-light-muted);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
}
.lp-support-fallback__phone i {
  color: var(--lp-light-accent);
  font-size: 0.78rem;
}
.lp-support-fallback__phone:focus-visible {
  outline: 2px solid var(--lp-light-accent);
  outline-offset: 4px;
  border-radius: 4px;
}

/* -----------------------------
   Reviews — stacked editorial quotes rather than a card grid.
   ----------------------------- */
/* Quotes in this section run from 55 to 226 characters, so the rows would
   otherwise sit at visibly different heights against each other.

   Two halves to keeping this steady, and only one of them is CSS. The
   selection is seeded by date in HomeView, so the same three reviews render
   all day instead of being re-sampled per request — that is what stops the
   section changing height between page loads. Here the rows are an
   equal-height grid, which levels them against each other whatever lands in
   them.

   Deliberately no worst-case reserve: with the content settled, the grid sizes
   to the tallest review actually on the page, so a day of short quotes does
   not pay for the longest one in the pool. The floor below only stops the band
   looking thin if every quote is a one-liner. */
.lp-reviews__list {
  display: grid;
  grid-auto-rows: 1fr;
  border-top: 1px solid var(--lp-dark-border);
}
.lp-review-row {
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* A floor, not a reserve. The quote and name stay together as a pair and the
     pair is centred, so a short review reads as deliberately set rather than
     as a gap above the name. */
  min-height: 9.5rem;
  padding: 26px 0;
  border-bottom: 1px solid var(--lp-dark-border);
  text-decoration: none;
  color: inherit;
}
.lp-review-row__text {
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.6;
  color: var(--lp-dark-ink);
  margin: 0 0 14px;
}
.lp-review-row__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--lp-dark-muted);
}
.lp-review-row__stars {
  display: inline-flex;
  gap: 2px;
  font-size: 0.72rem;
  color: #f5b301;
}
.lp-review-row__stars .lp-review-row__star--empty { color: var(--lp-dark-border); }

/* Narrower column, same words, so the floor rises a little with the wrap. */
@media (max-width: 767.98px) { .lp-review-row { min-height: 10.5rem; } }

/* -----------------------------
   About / company capability
   ----------------------------- */
.lp-about__logo {
  width: clamp(68px, 9vw, 104px);
  margin-bottom: 20px;
}
.lp-about__logo img {
  width: 100%;
  height: auto;
  display: block;
}
.lp-about__lede {
  max-width: 68ch;
  margin: 12px 0 36px;
  line-height: 1.6;
  color: var(--lp-light-muted);
}
.lp-company-capability {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 28px;
}
.lp-company-capability__item {
  border-top: 2px solid var(--brand-dark);
  padding-top: 16px;
}
.lp-company-capability__item h3 { margin: 0 0 8px; font-size: 1.05rem; }
.lp-company-capability__item p { margin: 0; color: var(--lp-light-muted); font-size: 0.92rem; line-height: 1.55; }

/* -----------------------------
   Quote / contact band
   ----------------------------- */
.lp-quote-band { padding-top: clamp(56px, 8vw, 100px); }

.lp-forms-head { margin-bottom: 40px; }
.lp-forms-head .cme-section-subtitle { max-width: 60ch; }

.lp-quote-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(280px, 1fr);
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}

/* Segmented control that swaps which form panel is visible below it —
   the callback form takes over the quote form's own slot rather than
   living as a second form alongside it. */
.lp-form-toggle {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  margin-bottom: 24px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--lp-dark-border);
  border-radius: 999px;
}
.lp-form-toggle__btn {
  appearance: none;
  border: none;
  background: none;
  cursor: pointer;
  padding: 10px 22px;
  border-radius: 999px;
  font: inherit;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--lp-dark-muted);
  transition: background var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui);
}

.lp-form-toggle__btn.is-active {
  background: var(--brand);
  color: #fff;
}
.lp-form-toggle__btn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* -----------------------------
   Enquiry wizard — "quote or callback?" choice screen + one-question-at-a-
   time step chrome. Shared by the landing page and every service-detail
   page (enquiry-wizard.js drives both via data-attributes, no per-page JS).
   ----------------------------- */
.lp-form-choice {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin-bottom: clamp(24px, 3vw, 32px);
}
.lp-form-choice__btn {
  appearance: none;
  cursor: pointer;
  text-align: left;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 20px;
  border-radius: 10px;
  border: 1px solid var(--lp-dark-border);
  background: rgba(255, 255, 255, 0.03);
  color: var(--lp-dark-ink);
  transition: border-color var(--motion-fast) var(--ease-ui), background var(--motion-fast) var(--ease-ui);
}

.lp-form-choice__btn.is-active {
  border-color: var(--brand);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
}
.lp-form-choice__btn i {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 1.05rem;
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 16%, transparent);
}
.lp-form-choice__btn > span {
  min-width: 0;
}
.lp-form-choice__btn strong {
  display: block;
  font-size: 1rem;
  margin-bottom: 2px;
}
.lp-form-choice__btn span.lp-form-choice__desc {
  display: block;
  font-size: 0.85rem;
  color: var(--lp-dark-muted);
  font-weight: 400;
}
/* These two are a single either/or choice — keep them side by side at
   every width rather than letting them stack, which read as two
   sequential steps instead of one decision. Below 560px there isn't
   room for both the icon and the description text, so the description
   drops and the icon shrinks instead of wrapping into a taller stack. */
@media (max-width: 560px) {
  .lp-form-choice__btn { padding: 14px 12px; gap: 10px; }
  .lp-form-choice__btn i { width: 32px; height: 32px; font-size: 0.9rem; }
  .lp-form-choice__btn strong { font-size: 0.9rem; margin-bottom: 0; }
  .lp-form-choice__btn span.lp-form-choice__desc { display: none; }
}

/* Tile selector — replaces a native <select> with icon buttons that set a
   hidden input's value. Used for service area / what needs help / urgency,
   all of which are short enumerated choices better shown as visual options
   than a dropdown. */
.lp-tile-select {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}
.lp-tile-select__btn {
  appearance: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--lp-dark-border);
  background: rgba(255, 255, 255, 0.03);
  color: var(--lp-dark-ink);
  font-size: 0.88rem;
  font-weight: 600;
  text-align: left;
  transition: border-color var(--motion-fast) var(--ease-ui), background var(--motion-fast) var(--ease-ui);
}

.lp-tile-select__btn.is-active {
  border-color: var(--brand);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
}
.lp-tile-select__btn i {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 0.85rem;
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 16%, transparent);
}
.lp-tile-select__error {
  display: none;
  margin: 8px 0 0;
  font-size: 0.82rem;
  color: #f87171;
}
.lp-tile-select-group.has-error .lp-tile-select__btn {
  border-color: #f87171;
}
.lp-tile-select-group.has-error .lp-tile-select__error {
  display: block;
}

/* Step chrome: icon + title + progress, sitting above whichever step is
   currently visible. Built once per wizard-enabled form by JS. */
.lp-wizard-chrome { margin-bottom: 24px; }
.lp-wizard-progress {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.lp-wizard-progress__step {
  flex: 0 0 auto;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--lp-dark-muted);
  white-space: nowrap;
}
.lp-wizard-progress__track {
  flex: 1 1 auto;
  height: 3px;
  border-radius: 999px;
  background: var(--lp-dark-border);
  overflow: hidden;
}
.lp-wizard-progress__fill {
  height: 100%;
  background: var(--brand);
  border-radius: 999px;
  transition: width var(--motion-ui) var(--ease-ui);
}
.lp-wizard-head {
  display: flex;
  align-items: center;
  gap: 14px;
}
.lp-wizard-head__icon {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--brand) 16%, transparent);
  color: var(--brand);
  font-size: 1.1rem;
}
.lp-wizard-head__title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  /* Pinned so the heading level can change for document-outline reasons
     without altering how the step title looks. */
  line-height: 1.2;
}
.lp-wizard-head__optional {
  font-weight: 400;
  font-size: 0.82em;
  color: var(--lp-dark-muted);
}

.lp-wizard-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 24px;
}
.lp-wizard-nav__back { order: 1; }
.lp-wizard-nav__next { order: 2; margin-left: auto; }

@media (max-width: 480px) {
  .lp-wizard-head__title { font-size: 1.05rem; }
}

/* The shared .cme-field markup (used by every wizard step) defaults to
   solid white inputs sized for a light card — inside the quote band's
   dark section that reads as a jarring, uncalibrated block. Restyle to
   the same translucent bordered surface used elsewhere on this page
   (tile-select buttons, contact rows). */
.lp-dark-section .cme-field :is(input, select, textarea) {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--lp-dark-border);
  color: var(--lp-dark-ink);
}
.lp-dark-section .cme-field :is(input, select, textarea)::placeholder {
  color: var(--lp-dark-muted);
}
.lp-dark-section .cme-field :is(input, select, textarea):focus {
  background: rgba(255, 255, 255, 0.07);
  border-color: var(--brand);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 18%, transparent);
}
.lp-dark-section .cme-field.is-invalid :is(input, select, textarea) {
  border-color: #f87171;
  box-shadow: 0 0 0 4px rgba(248, 113, 113, .14);
}

/* Terms/privacy toggles — the shared pill-slider is tuned for a light
   card and is nearly invisible at low opacity against a dark background. */
.lp-dark-section .switch-group { padding: 6px 0; }
.lp-dark-section .terms-text { color: var(--lp-dark-ink); }
.lp-dark-section .terms-text a {
  color: var(--brand);
  text-decoration-color: color-mix(in srgb, var(--brand) 40%, transparent);
}
.lp-dark-section .pill-slider {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--lp-dark-border);
}
.lp-dark-section .pill-slider::after { background: var(--lp-dark-muted); }
.lp-dark-section .pill-checkbox:checked + .pill-slider {
  background: color-mix(in srgb, var(--brand) 35%, transparent);
  border-color: var(--brand);
}
.lp-dark-section .pill-checkbox:checked + .pill-slider::after { background: #fff; }

.lp-dark-section .cme-turnstile { margin-top: 18px; }

.lp-quote-grid__contact {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--lp-dark-border);
  border-radius: 8px;
  padding: 28px;
}
.lp-contact__lede { color: var(--lp-dark-muted); margin: 0 0 20px; }

.lp-contact__actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.lp-contact__row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border-radius: 8px;
  border: 1px solid var(--lp-dark-border);
  background: rgba(255, 255, 255, 0.03);
  text-decoration: none;
  transition: border-color var(--motion-fast) var(--ease-ui), background var(--motion-fast) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
}

.lp-contact__row-icon {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 1.05rem;
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 16%, transparent);
}
.lp-contact__row-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.lp-contact__row-text strong {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--lp-dark-ink);
}
.lp-contact__row-text span {
  font-size: 0.88rem;
  color: var(--lp-dark-muted);
}

/* -----------------------------
   Footer additions
   ----------------------------- */
.lp-footer__bitcoin {
  font-size: 0.78rem;
  color: var(--muted);
  margin: 8px 0 0;
  text-align: center;
}
.lp-footer__legal { flex-wrap: wrap; row-gap: 6px; }
.lp-footer__legal span { font-size: 0.78rem; }

/* -----------------------------
   Responsive
   ----------------------------- */
@media (max-width: 900px) {
  .lp-capability { grid-template-columns: 56px 1fr; }
  .lp-quote-grid { grid-template-columns: 1fr; }

  .lp-hero__content {
    padding-top: clamp(88px, 16vw, 118px);
    padding-bottom: 32px;
  }
}

@media (max-width: 650px) {
  .lp-hero__content {
    padding-top: 96px;
    padding-bottom: 12px;
  }
  .lp-hero__title { font-size: 2.1rem; }
  .lp-hero__actions { flex-direction: column; align-items: stretch; }
  .lp-hero .lp-btn--lg { --lp-btn-h: 50px; padding-inline: 22px; font-size: 0.95rem; }
  .lp-hero__explore {
    position: absolute;
    left: 50%;
    bottom: max(18px, env(safe-area-inset-bottom, 0px) + 14px);
    max-width: calc(100% - (var(--cme-section-gx) * 2));
    margin: 0;
    padding: 10px 8px;
    font-size: 0.66rem;
    letter-spacing: 0.1em;
    transform: translate(-50%, 0);
  }

  /* Base site hides .cme-hero__trust below 650px; keep the Bitcoin
     pill visible here by pulling it into normal flow under the CTAs. */
  .lp-hero .cme-hero__trust {
    display: inline-flex !important;
    position: static;
    transform: none;
    /* Static here, so the `right` offset above no longer applies and the
       chip sat flush against the screen edge while every other element on
       the page kept its gutter. */
    margin: 16px 0 74px;
    padding-inline: var(--cme-section-gx);
    justify-content: center;
  }

  /* The eyebrow right below this already centres itself (base.css, same
     640px breakpoint) — without this the logo stays left-aligned above
     centred text, which reads as a mismatch. */
  .lp-about__logo { margin-inline: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .lp-btn,
  .lp-hero__explore,
  .lp-hero__explore-arrow,
  .lp-hero__explore-label::after {
    animation: none;
    transition: none;
  }
}

/* -----------------------------
   Cross-document View Transition — Explore continuity between the active
   Engineering Capabilities panel here and the matching /services/<slug>/
   hero. This file is linked by both landing_preview.html and
   service_detail.html, so the opt-in below and the hero-copy slide
   keyframes are declared once here rather than duplicated in
   service-detail.css (which only carries the view-transition-name
   assignments specific to its own markup).

   Progressive enhancement only: browsers without View Transitions support
   silently ignore every rule in this block and get plain, immediate
   navigation — nothing here can block or delay the actual page load, and
   the browser itself (not this code) already declines to run the
   transition for modifier-key clicks, middle-click, cross-origin links,
   or any navigation the destination document doesn't equally opt into. */
@view-transition {
  navigation: auto;
}

/* Media stays visually stable across the navigation — named on the
   *container*, not the individual <video>, and deliberately unanimated
   (relies on the browser's default cross-fade/resize morph). This is
   what keeps the dark overlay pinned to the video: the container also
   holds the ::after scrim below, so both are captured together as one
   flat "the video, darkened" snapshot on each side, rather than the
   video going into its own named group while the scrim gets swept into
   the generic root cross-fade (which would make the fade visibly
   brighten/shift independently of the video underneath it — the
   background darkening would appear to move, which is exactly what must
   not happen here). There's only ever one such container per page, so no
   per-category name is needed to keep it uniquely matched — correctness
   for which category's video shows through comes from the Explore link
   itself always pointing at the currently active panel's own category,
   not from the transition-naming. */
.lp-capability-carousel__media { view-transition-name: hero-media; }

/* The active panel's copy — matched against .lp-svc-hero__content on the
   detail page (service-detail.css) under the same name. Given an explicit
   directional slide rather than the default morph, since the two sides
   differ enough in size/position that a smooth resize-interpolation would
   read as a shape-shift rather than a clean horizontal swipe. One name
   works for all three categories here (and for the reverse/back-button
   direction) since exactly one panel is ever visible at a time — see the
   note in service-detail.css about not building direction-aware variants. */
.lp-capability-panel:not([hidden]) { view-transition-name: hero-copy; }

::view-transition-old(hero-copy) {
  animation: var(--motion-ui) var(--ease-ui) both lp-vt-copy-out;
}
::view-transition-new(hero-copy) {
  animation: var(--motion-ui) var(--ease-premium) both lp-vt-copy-in;
}
@keyframes lp-vt-copy-out {
  to { transform: translateX(-36px); opacity: 0; }
}
@keyframes lp-vt-copy-in {
  from { transform: translateX(36px); opacity: 0; }
}

/* Reduced motion: no slide, no default cross-fade morph either — an
   instant swap. Navigation itself was never gated on the animation (the
   API doesn't work that way), so there's nothing to "skip forward" here,
   just nothing left to animate. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* -----------------------------
   Floating call action (landing page only)
   -----------------------------
   A small utility control, not a chat widget: navy glass orb that expands to
   "Call now" on hover/focus. One universally legible treatment rather than
   theming it per section — nav-context.js reports what sits under the
   *navbar*, which is the wrong end of the viewport for a bottom-right
   control, and inventing a second scroll system for one button is exactly
   the brittleness worth avoiding. Navy-on-light and navy-plus-light-ring-on-
   dark both hold contrast across every band on the page.

   z-index 1050 is deliberate, from the existing stack: above page content
   and .cme-scroll (1000), below the toast (1080), navbar (1100), mobile
   menu/overlay (1190-1201), submit modal (1800) and cookie banner (3000). */
.lp-callnow {
  position: fixed;
  right: max(20px, env(safe-area-inset-right, 0px));
  bottom: max(20px, env(safe-area-inset-bottom, 0px));
  z-index: 1050;

  display: inline-flex;
  align-items: center;
  gap: 0;
  height: 56px;
  width: 56px;
  padding: 0;
  border-radius: 999px;
  overflow: hidden;
  text-decoration: none;
  white-space: nowrap;

  background: color-mix(in srgb, #0b1521 82%, transparent);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(244, 247, 251, 0.22);
  box-shadow: 0 8px 22px rgba(3, 21, 61, 0.28);
  color: #f4f7fb;

  /* Fades in rather than snapping — .is-ready is set by call-now.js after a
     short beat, or on first scroll, whichever comes first. */
  opacity: 0;
  transform: scale(0.9);
  pointer-events: none;
  transition:
    width var(--motion-ui) var(--ease-ui),
    opacity var(--motion-ui) var(--ease-ui),
    transform var(--motion-ui) var(--ease-ui),
    border-color var(--motion-fast) var(--ease-ui),
    background var(--motion-fast) var(--ease-ui);
}
.lp-callnow.is-ready {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

.lp-callnow__icon {
  flex: 0 0 54px;
  display: grid;
  place-items: center;
  font-size: 1.02rem;
  color: var(--brand);
  transition: transform var(--motion-ui) var(--ease-ui);
}
.lp-callnow__label {
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0;
  transform: translateX(-4px);
  padding-right: 20px;
  transition: opacity var(--motion-fast) var(--ease-ui) var(--motion-step), transform var(--motion-ui) var(--ease-ui) var(--motion-step);
}

/* Hover/focus expansion. Fixed-position, so widening cannot shift layout. */
@media (hover: hover) and (pointer: fine) {
  .lp-callnow.is-ready:hover,
  .lp-callnow.is-ready:focus-visible {
    width: 156px;
    background: color-mix(in srgb, #0b1521 90%, transparent);
    border-color: rgba(244, 247, 251, 0.38);
  }
  .lp-callnow.is-ready:hover .lp-callnow__label,
  .lp-callnow.is-ready:focus-visible .lp-callnow__label {
    opacity: 1;
    transform: translateX(0);
  }
}
/* Keyboard users on any device get the expanded state too. */
.lp-callnow.is-ready:focus-visible {
  width: 156px;
  outline: 2px solid var(--brand);
  outline-offset: 3px;
}
.lp-callnow.is-ready:focus-visible .lp-callnow__label {
  opacity: 1;
  transform: translateX(0);
}

/* One-off attention cue, driven by call-now.js on a cooldown. Transform-only
   so it cannot touch layout. */
@keyframes lp-callnow-nudge {
  0%   { transform: scale(1) translateY(0); }
  35%  { transform: scale(1.04) translateY(-3px); }
  70%  { transform: scale(1.01) translateY(0); }
  100% { transform: scale(1) translateY(0); }
}
.lp-callnow.is-nudging { animation: lp-callnow-nudge var(--motion-hero) var(--ease-premium); }
.lp-callnow.is-nudging .lp-callnow__icon { transform: rotate(-9deg); }

/* Phones: no hover, so the orb stays collapsed and a single tap dials. */
@media (max-width: 640px) {
  .lp-callnow {
    height: 52px;
    width: 52px;
    right: max(16px, env(safe-area-inset-right, 0px));
    bottom: max(16px, env(safe-area-inset-bottom, 0px));
  }
  .lp-callnow__icon { flex-basis: 50px; }
}

/* The consent banner is fixed to the bottom of the viewport at z-index 3000
   and would sit on top of this. Same resolution as the service-detail explore
   cue: yield while consent is pending rather than peeking out from behind it. */
body:has(#cookie-banner:not([hidden])) .lp-callnow {
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .lp-callnow {
    /* width collapses instantly (1ms, not 0) so the expand/contract is
       gone while opacity still cross-fades rather than snapping. */
    transition: opacity var(--motion-fast) var(--ease-ui), width 1ms linear, background var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui);
    transform: none;
  }
  .lp-callnow.is-ready { transform: none; }
  .lp-callnow.is-nudging { animation: none; }
  .lp-callnow.is-nudging .lp-callnow__icon { transform: none; }
  .lp-callnow__label { transition: opacity var(--motion-fast) var(--ease-ui); transform: none; }
}

/* -----------------------------
   Hover states, gated to devices that actually hover
   -----------------------------
   On touch, :hover latches when you tap and stays applied until you tap
   something else — so a tapped link kept its underline, and buttons kept
   their lifted transform, long after the tap. Every rule below is pure
   :hover (no :focus-visible was folded in), so gating them cannot affect
   keyboard focus, which stays available on every device. */
@media (hover: hover) and (pointer: fine) {
  .lp-hero__explore:hover {
    color: var(--brand, #38bdf8);
  }
  .lp-hero__explore:hover .lp-hero__explore-arrow {
    transform: translateY(3px);
    color: var(--brand);
  }
  .lp-hero__explore:hover .lp-hero__explore-label::after {
    transform: scaleX(1);
  }
  .lp-btn:hover { transform: translateY(-1px); }
  .lp-btn--ghost:hover { background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.5); }
  a.lp-link:hover::after { transform: scaleX(1); }
  /* :not(.is-active) matters — the hover colour is dimmer than the active
     colour, so without it, pointing at the selected row faded it from full
     white to 85%. Hovering the current item should do nothing. */
  .lp-capability-tab:not(.is-active):hover { color: rgba(244, 247, 251, 0.85); }
  .lp-capability-tab:not(.is-active):hover .lp-capability-tab__arrow { opacity: 1; }
  .lp-support-card .lp-btn--primary:hover { background: color-mix(in srgb, var(--lp-light-ink) 84%, var(--brand) 16%); }
  .lp-support-card__link:hover { color: var(--brand-dark); }
  .lp-support-fallback__phone:hover { color: var(--lp-light-accent-strong); }
  .lp-form-toggle__btn:hover { color: var(--lp-dark-ink); }
  .lp-form-choice__btn:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--lp-dark-border)); }
  .lp-tile-select__btn:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--lp-dark-border)); }
  .lp-contact__row:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--lp-dark-border)); background: color-mix(in srgb, var(--brand) 8%, transparent); transform: translateY(-1px); }
}

/* -----------------------------
   Scroll reveal
   -----------------------------
   Two variants, applied to whole compositions — never to bullets, list rows,
   individual cards, footer links or legal copy. The page should not visibly
   build itself as you scroll past it.

     .lp-reveal        a composition. The group is only the trigger; its
                       direct children carry the motion, half a step apart,
                       so an eyebrow + heading + lede settle as one block
                       rather than as three text nodes taking turns. The
                       Support grid uses the same rule, which is why its
                       four panels arrive as a wave and not as four widgets.

     .lp-reveal--soft  media. Opacity only, no travel — imagery should feel
                       like it becomes available, not like it enters.

   Reveal happens once; scrolling back never replays it (see reveal.js).

   Baseline is visible: .lp-reveal only hides anything while .lp-reveal-ready
   is on <html>. That class is set in <head> by includes/motion_head.html —
   early enough that a group is never painted and then hidden — and dropped
   again on load if reveal.js did not claim it. A blocked or broken script
   therefore leaves every section rendering normally rather than blank, which
   is the failure mode of a JS-gated reveal. */
.lp-reveal-ready .lp-reveal:where(:not(.lp-reveal--soft, .lp-capability-panels)) > * {
  opacity: 0;
}

.lp-reveal-ready .lp-reveal--soft { opacity: 0; }
.lp-reveal-ready .lp-reveal--soft.is-revealed {
  opacity: 1;
  transition: opacity var(--motion-ui) var(--ease-ui);
}

@media (prefers-reduced-motion: reduce) {
  /* Genuinely static: no travel, no staged delays, content simply present.
     Not "1ms durations with the sequencing left in". */
  /* Every selector the reveal uses is repeated here, not a shorter version
     of it: .lp-reveal.is-revealed > * carries more classes than
     .lp-reveal > *, so a tidier override loses on specificity and the
     animation runs anyway. */
  /* Every selector the role system uses is repeated, not a shorter version of
     it: the role rules carry more classes than a generic override would, so a
     tidier rule loses on specificity and the animation runs anyway. */
  .lp-reveal-ready .lp-reveal > *,
  .lp-reveal-ready .lp-reveal:not(.lp-reveal--soft):not(.lp-capability-panels).is-revealed > *,
  .lp-reveal-ready .lp-reveal.is-revealed > :is(.lp-eyebrow, .cme-eyebrow, .lp-editorial-head__eyebrow),
  .lp-reveal-ready .lp-reveal.is-revealed > :is(h1, h2, h3, .cme-section-title),
  .lp-reveal-ready .lp-reveal.is-revealed > :is(.lp-hero__rating, .lp-hero__actions, .lp-mobile-service__actions, .lp-support-fallback__actions, .lp-bitcoin__actions),
  .lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *,
  .lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(1),
  .lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(2),
  .lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(3),
  .lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(4),
  .lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(5),
  .lp-reveal-ready .lp-reveal.lp-reveal--stack.is-revealed > *:nth-child(6),
  .lp-reveal-ready .lp-reveal.is-revealed > .lp-capability-topbar__hint,
  .lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > *,
  .lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > h3,
  .lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > .lp-capability-story__actions,
  .lp-reveal-ready .lp-capability-panels.lp-reveal.is-revealed .lp-capability-panel:not([hidden]) > ul > li,
  .lp-reveal-ready .lp-reveal--soft,
  .lp-reveal-ready .lp-reveal--soft.is-revealed {
    opacity: 1;
    transform: none;
    animation: none;
    transition: none;
  }
}

/* -----------------------------
   Button radius
   -----------------------------
   Every button in this experience is a 6px rectangle except the wizard's
   submit — the single most important control in the enquiry flow — which was
   a 999px pill, because .cme-btn/.cme-cta-btn/.hero-cta carry that radius as
   an !important in base.css for the original homepage. Matching the
   !important is the only way to override it; scoping to .lp-page leaves that
   homepage exactly as it is. Declared here rather than in service-detail.css
   because this file is the one both page types load. */
/* The compact button is 39-40px, which is right for a dense desktop row but
   under the 44px comfortable-target size on a phone — and the capability
   panel's "Request a quotation" is a real, visible primary action there.
   Gated on a coarse pointer so the desktop row keeps its proportions. */
@media (pointer: coarse) {
  .lp-btn--sm { min-height: 44px; }
}

.lp-page .cme-btn,
.lp-page .cme-cta-btn,
.lp-page .hero-cta,
/* The submission modal is a shared overlay declared in base.html, outside
   .lp-page — but it is the last screen of this flow, so its Done button
   belongs to the same system. :has() is progressive: without it the modal
   simply keeps base.css's pill. */
body:has(.lp-page) .cme-submit-modal .cme-btn {
  border-radius: 6px !important;
}

.lp-page .cme-btn,
body:has(.lp-page) .cme-submit-modal .cme-btn {
  min-height: 48px;
  height: auto;
  padding: 0 22px;
  gap: 8px;
  line-height: 1.15;
}
.lp-page .cme-btn--lg {
  min-height: 52px;
  padding-inline: 24px;
  font-size: 1rem;
}

/* -----------------------------
   Legacy fragment anchors
   -----------------------------
   The compatibility anchors reuse the old homepage's fragment IDs so inbound
   links keep working, which means they also inherit any CSS that still targets
   those IDs. base.css styles #reviews as a full section (it was one), and that
   gave a zero-height anchor a 900px box — the jump landed a screen short of
   the section it points at.

   Anchors are pure navigation targets: no box, ever, whatever an ID selector
   elsewhere believes about them. */
.lp-page .cme-anchor {
  display: block;
  /* !important because the rules being overridden are ID selectors
     (base.css's `#reviews { padding-top: 6rem; min-height: 100vh }`), which
     outrank any class-based override. Matching that specificity by listing
     each alias ID here would mean editing this rule every time an anchor is
     added; the intent is simply that an anchor never has a box. */
  min-height: 0 !important;
  height: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  background: none !important;
}


/* -----------------------------
   Error pages (404)
   -----------------------------
   The 500 page is deliberately not styled here — it is a standalone file with
   its own inline CSS so that it cannot be broken by a failing static pipeline.
   See templates/500.html.

   A single centred column rather than a section composition: there is nothing
   to compose against, and a full-height marketing band would overstate what
   is really a wayfinding page. */
.lp-error {
  display: flex;
  align-items: center;
  min-height: 78vh;
  min-height: 78svh;
  padding-top: var(--nav-h, 72px);
}

.lp-error__content > * { max-width: 640px; }

.lp-error__title {
  margin: 0 0 16px;
  font-size: clamp(2rem, 5vw, 3.1rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.01em;
}

.lp-error__lede {
  margin: 0;
  font-size: 1.05rem;
  color: var(--lp-dark-muted);
}

.lp-error__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 28px;
}

/* Wraps to one link per line on a phone, where a row of four would either
   overflow or shrink below a comfortable tap target. */
.lp-error__links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  margin-top: 36px;
  padding-top: 24px;
  border-top: 1px solid rgba(148, 163, 184, 0.18);
}

.lp-error__links .lp-link {
  color: var(--lp-dark-muted);
  font-size: 0.92rem;
  padding: 6px 0;
}
.lp-error__links .lp-link:hover { color: var(--lp-dark-ink); }
