/* Opt-in page components (hero, story, section head, CTA band, FAQ, media strip,
 * link card, caption, footer). Load order: tokens.css, sections.css, then this
 * file, then the fork's optional brand.css (see site/partials/design-head.php).
 * Pages that use none of these classes are unchanged.
 *
 * Every value is a theme token with a fallback chain, so a customer re-skins a
 * component from brand.css without editing this file. Names follow
 * --mv-<component>-<property>, e.g. --mv-hero-bg, --mv-cta-bg, --mv-footer-bg.
 * No fixed colors except fallbacks: both themes work through tokens.css
 * (--text, --surface, --accent-text, ...). The hex fallbacks only guard a fork
 * that lacks a token; the truth lives in tokens.css. Verify dark mode when
 * overriding a complete pair (background and text together).
 *
 * Section tone: headings and text inside .mv-section follow the section's tone
 * (--_mv-text and --_mv-muted from sections.css), so inverse and accent
 * sections stay readable. Outside a section the plain --text tokens apply.
 * Blocks with their own surface (FAQ, link card, footer, CTA) keep their own
 * colours.
 *
 * Component rules:
 * - Headline sizes use the profile page's type scale.
 * - The FAQ chevron respects prefers-reduced-motion.
 * - Section padding, tone and waves stay with .mv-section (sections.css). Put
 *   these components inside .mv-section__inner instead of a second section class.
 *
 * Full-bleed hero (.mv-hero): the colour band spans the viewport via
 * box-shadow spread plus clip-path. Neither contributes to scrollable overflow,
 * so no horizontal scrollbar appears and no overflow-x on an ancestor is needed
 * (measured at 1280 and 393 px). Set --mv-hero-bg to enable it; the default is
 * transparent, so existing pages stay unchanged. --mv-hero-bg must be a plain
 * colour: it also feeds the box-shadow, and a gradient makes that declaration
 * invalid, so the full-width strip is silently missing (the hero box itself
 * still paints). Use .mv-hero--accent for a ready, readable accent hero.
 *
 * Media strip (.mv-media-strip) markup contract: the strip is a scroll container
 * and must be keyboard reachable:
 *   <div class="mv-media-strip" role="region" aria-label="..." tabindex="0">
 * Children: .mv-media-strip__item.
 *
 * Link card (.mv-linkcard) markup contract: a navigation landmark labelled by
 * its heading:
 *   <nav class="mv-linkcard" aria-labelledby="x"><h3 id="x">...</h3>
 *   <ul class="mv-linkcard__links"><li><a href="...">...</a></li></ul></nav>
 *
 * Footer (.mv-footer): CSS only, no partial. Elements: __inner, __brand, __name,
 * __tagline, __social, __badge, __legal, __copy. Parts may be omitted.
 */

/* ---------- eyebrow ------------------------------------------------------- */
.mv-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--mv-eyebrow-bg, var(--_mv-eyebrow-bg, var(--accent-soft, rgba(12, 110, 109, .12))));
  color: var(--mv-eyebrow-text, var(--_mv-eyebrow-text, var(--accent-text, #0c6e6d)));
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
/* The pill follows the section tone. On a filled accent or inverse section the
 * default pair (accent tint, accent text) is invisible, so the pill inverts:
 * section text colour as the surface, section background as the ink. This stays
 * a complete pair in both themes because both come from the same section.
 * The chain is --mv-eyebrow-* (customer) first, then --_mv-eyebrow-* (tone),
 * then the theme tokens (default and soft sections). Limit: this needs a plain
 * colour as the section background. If a fork sets --mv-accent-bg or
 * --mv-inverse-bg to a gradient, the "background" token resolves to no colour and
 * the pill becomes text-coloured on text-coloured (white on white); such a fork
 * must also set --mv-eyebrow-text to a solid ink colour. */
.mv-section[data-tone="accent"] .mv-eyebrow {
  --_mv-eyebrow-bg: var(--_mv-text, #fff);
  --_mv-eyebrow-text: var(--_mv-bg, #0c6e6d);
}
.mv-section[data-tone="inverse"] .mv-eyebrow {
  --_mv-eyebrow-bg: var(--_mv-text, #fff);
  --_mv-eyebrow-text: var(--_mv-bg, #14202b);
}
/* Inside a card the surface is the card, not the section: light tint with the
 * card ink (the card is light in accent and inverse sections, see sections.css). */
.mv-section[data-tone="accent"] .mv-card .mv-eyebrow,
.mv-section[data-tone="inverse"] .mv-card .mv-eyebrow {
  --_mv-eyebrow-bg: rgba(12, 110, 109, .12);
  --_mv-eyebrow-text: var(--_mv-card-text, #14202b);
}

/* A .mv-card is its own surface (sections.css sets --_mv-card and
 * --_mv-card-text, and already resets link, focus and action for it).
 * It does not reset --_mv-text, so a component inside a card on an accent or
 * inverse section would take white text onto the white card. The reset gives
 * components in a card the card ink. --_mv-muted needs the same care: sections.css
 * pins it to a fixed light-theme grey, which is unreadable on the dark card of a
 * default or soft section in the dark theme (2.36:1). So the muted colour follows
 * the theme token (--text-2) there, and accent and inverse sections, whose card
 * stays white, keep the fixed dark grey. Pages that use .mv-card without a
 * component are unaffected: nothing else reads these below the section element. */
.mv-section .mv-card {
  --_mv-text: var(--_mv-card-text, #14202b);
  --_mv-muted: var(--mv-default-card-muted, var(--mv-card-muted, var(--text-2, #4a5c68)));
}
.mv-section[data-tone="soft"] .mv-card {
  --_mv-muted: var(--mv-soft-card-muted, var(--mv-card-muted, var(--text-2, #4a5c68)));
}
.mv-section[data-tone="accent"] .mv-card {
  --_mv-muted: var(--mv-accent-card-muted, var(--mv-card-muted, #4a5c68));
}
.mv-section[data-tone="inverse"] .mv-card {
  --_mv-muted: var(--mv-inverse-card-muted, var(--mv-card-muted, #4a5c68));
}

/* ---------- hero ---------------------------------------------------------- */
.mv-hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  box-sizing: border-box;
  max-width: var(--maxw, 1200px);
  margin: 0 auto;
  --_mv-hero-pb: 56px;
  padding-block: 40px var(--_mv-hero-pb, 56px);
  padding-inline: 24px;
  background: var(--mv-hero-bg, transparent);
  color: var(--mv-hero-text, var(--_mv-text, var(--text, #14202b)));
  box-shadow: 0 0 0 100vmax var(--mv-hero-bg, transparent);
  clip-path: inset(0 -100vmax);
}
.mv-hero__media {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-media, var(--radius-lg, 14px));
  box-shadow: var(--shadow-2, 0 4px 12px rgba(10, 37, 64, .1));
}
.mv-hero__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: var(--mv-media-position, center 20%);
  border-radius: 0;
}
.mv-hero__copy {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.mv-hero h1 {
  margin: 0;
  color: var(--mv-hero-heading, var(--mv-hero-text, var(--_mv-text, var(--text, #14202b))));
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-size: clamp(2rem, 5vw, 2.7rem);
  line-height: 1.14;
  text-wrap: balance;
}
.mv-hero__lead {
  max-width: 46ch;
  margin: 0;
  color: var(--mv-hero-muted, var(--_mv-muted, var(--text-2, #4a5c68)));
  font-size: clamp(1.05rem, 2.4vw, 1.15rem);
}
@media (min-width: 860px) {
  .mv-hero { grid-template-columns: 1fr 1fr; align-items: center; }
  .mv-hero__media { order: 2; }
  .mv-hero__copy { order: 1; }
}
/* Filled accent hero: a complete, readable colour set in one class. Every
 * value is overridable (--mv-hero-bg, -text, -heading, -muted, -eyebrow-bg,
 * -eyebrow-text). The band is a plain colour; see the full-bleed note above. */
.mv-hero--accent {
  background: var(--mv-hero-bg, var(--accent, #0c6e6d));
  color: var(--mv-hero-text, var(--on-accent, #fff));
  box-shadow: 0 0 0 100vmax var(--mv-hero-bg, var(--accent, #0c6e6d));
}
.mv-hero--accent { --_mv-action-hover: var(--mv-hero-action-hover, var(--mv-hero-action-bg, var(--on-accent, #fff))); }
.mv-hero--accent h1 { color: var(--mv-hero-heading, var(--mv-hero-text, var(--on-accent, #fff))); }
.mv-hero--accent .mv-hero__lead { color: var(--mv-hero-muted, var(--mv-hero-text, var(--on-accent, #fff))); }
.mv-hero--accent .mv-eyebrow {
  background: var(--mv-hero-eyebrow-bg, var(--on-accent, #fff));
  color: var(--mv-hero-eyebrow-text, var(--accent-text, #0c6e6d));
}
/* Actions and links on the accent surface. A primary button in the section
 * action colour would equal the hero surface (accent on accent), and the
 * default link colour is the accent as text, so both get their own pair, like
 * the CTA band: light button with accent text, links in the hero text colour
 * (underline kept), focus ring in the hero text colour. */
.mv-hero--accent :is(.btn-primary, .mv-button) {
  background: var(--mv-hero-action-bg, var(--on-accent, #fff));
  color: var(--mv-hero-action-text, var(--accent-text, #0c6e6d));
}
.mv-hero--accent :is(.btn-primary, .mv-button):hover {
  background: var(--_mv-action-hover, var(--mv-hero-action-bg, var(--on-accent, #fff)));
}
.mv-hero--accent a:not(.mv-button):not(.btn):not(.btn-primary) {
  color: var(--mv-hero-link, var(--mv-hero-text, var(--on-accent, #fff)));
  text-decoration: underline;
  text-underline-offset: .18em;
}
.mv-hero--accent :is(a, button):focus-visible {
  outline: 3px solid var(--mv-hero-text, var(--on-accent, #fff));
  outline-offset: 3px;
}

/* Hero edge: inside .mv-section__inner the hero uses the same
 * edge as the section head below it. The 24px inline padding stays only for a
 * hero that stands on its own, outside a section container. The full-bleed band
 * is box-shadow plus clip-path and does not depend on this padding. */
.mv-section__inner .mv-hero { padding-inline: 0; }

/* Hero variants: data-layout, data-scale, data-media on .mv-hero.
 * No attribute = today's split layout, 4:3 media, normal heading scale. */
.mv-hero[data-layout="centered"] { text-align: center; }
.mv-hero[data-layout="centered"] .mv-hero__copy { align-items: center; }
.mv-hero[data-layout="centered"] .mv-hero__lead { margin-inline: auto; }
.mv-hero[data-layout="centered"] .mv-eyebrow { align-self: center; }
.mv-hero[data-layout="centered"] .mv-hero__media { width: 100%; }
@media (min-width: 860px) {
  .mv-hero[data-layout="centered"] { grid-template-columns: 1fr; }
  .mv-hero[data-layout="centered"] .mv-hero__media { order: 2; }
  .mv-hero[data-layout="split-rev"] .mv-hero__media { order: 1; }
  .mv-hero[data-layout="split-rev"] .mv-hero__copy { order: 2; }
}
.mv-hero[data-scale="large"] h1 { font-size: clamp(2.4rem, 6.2vw, 4rem); }
.mv-hero[data-media="arch"] .mv-hero__media {
  width: 100%;
  max-width: 26rem;
  justify-self: center;
  border-top-left-radius: 999px;
  border-top-right-radius: 999px;
}
.mv-hero[data-media="arch"] .mv-hero__media img { aspect-ratio: 4 / 5; }
.mv-hero[data-media="framed"] .mv-hero__media {
  width: 100%;
  max-width: 34rem;
  justify-self: center;
}
/* overlap: the image crosses the lower edge of its section by --mv-hero-overlap
 * (default 2rem) and stays above the next section. The image keeps its normal
 * place; instead the section gets shorter: the hero's bottom margin is negative
 * and equals hero padding + section padding + wave band + the overlap (the
 * overlap is ADDED to what is cut off, so the section ends the overlap above the
 * image's lower edge), all read from the tokens the section itself uses.
 * Preconditions for the overlap layout:
 *  - from 860px the columns are centred, so the text column must be lower than
 *    the image by at least 2 x the overlap (4rem at the default); with less the
 *    text crosses the section edge by overlap - slack / 2.
 *  - the next section needs top padding of at least overlap + 1rem (3rem at the
 *    default); with less the image reaches into its first block.
 *  - the hero is the last element of .mv-section__inner and the browser knows
 *    :has() (otherwise it renders like a plain hero).
 * The full-bleed band is switched off here, because clip-path would cut the
 * image; colour such a hero through the section tone. */
@supports selector(:has(a)) {
  .mv-section:has(> .mv-section__inner > .mv-hero[data-media="overlap"]:last-child) { z-index: 1; }
  .mv-section__inner > .mv-hero[data-media="overlap"]:last-child {
    box-shadow: none;
    clip-path: none;
    margin-bottom: calc(-1 * (var(--_mv-hero-pb, 56px) + var(--mv-section-space, clamp(3rem, 7vw, 6rem)) + var(--_mv-bottom, 0px) + var(--mv-hero-overlap, 2rem)));
  }
  .mv-section__inner > .mv-hero[data-media="overlap"]:last-child .mv-hero__media {
    width: 100%;
    max-width: 36rem;
    justify-self: center;
  }
}

/* ---------- hero video --------------------------------------------
 * .mv-hero-video: a hero whose medium is a muted loop video with a poster.
 * data-layout: full (default, no attribute needed), float, side-left,
 * side-right. Below 860px float and side-* stack, text first, medium below.
 * The poster is a real img (the LCP element), the video sits on top of it in
 * the same box and fades in once playing, so nothing shifts. Colours come from
 * tokens; every --mv-hero-video-* value is overridable in brand.css. On the
 * full layout the text sits on a scrim, whose colour is fixed light-on-dark in
 * both themes (text over a moving picture cannot follow the page theme). */
.mv-hero-video {
  --_mv-hv-scrim: var(--mv-hero-video-scrim, #0a2540);
  --_mv-hv-fg: var(--mv-hero-video-text, #fff);
  --_mv-hv-muted: var(--mv-hero-video-muted, #fff);
  --_mv-hv-eb-bg: var(--mv-hero-video-eyebrow-bg, #fff);
  --_mv-hv-eb-fg: var(--mv-hero-video-eyebrow-text, #0a2540);
  --_mv-hv-act-bg: var(--mv-hero-video-action-bg, #fff);
  --_mv-hv-act-fg: var(--mv-hero-video-action-text, #0a2540);
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: end;
  box-sizing: border-box;
  min-height: min(100svh, 52rem);
  background: var(--mv-hero-video-bg, var(--bg-2, #f2f5f6));
  color: var(--_mv-hv-fg, #fff);
}
.mv-hero-video .mv-hero-video__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.mv-hero-video__poster,
.mv-hero-video__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--mv-media-position, center 20%);
}
.mv-hero-video__video { opacity: 0; }
.mv-hero-video[data-playing] .mv-hero-video__video { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .mv-hero-video__video { transition: opacity 400ms ease; }
}
/* Scrim: strongest where the copy sits (bottom), at least 82% opacity in the
 * lower 60% of the hero, so white text keeps 4.5:1 even over a white video
 * frame as long as the copy stays in that part (min-height + bottom aligned). */
.mv-hero-video .mv-hero-video__media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--_mv-hv-scrim, #0a2540) 90%, transparent) 0%,
    color-mix(in srgb, var(--_mv-hv-scrim, #0a2540) 82%, transparent) 60%,
    color-mix(in srgb, var(--_mv-hv-scrim, #0a2540) 40%, transparent) 100%);
}
.mv-hero-video__inner {
  position: relative;
  z-index: 1;
  box-sizing: border-box;
  width: 100%;
  max-width: var(--maxw, 1200px);
  margin-inline: auto;
  padding-block: 96px 96px;
  padding-inline: 24px;
}
.mv-hero-video__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  min-width: 0;
}
.mv-hero-video__eyebrow {
  margin: 0;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--_mv-hv-eb-bg, #fff);
  color: var(--_mv-hv-eb-fg, #0a2540);
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.mv-hero-video__headline {
  margin: 0;
  color: var(--_mv-hv-fg, #fff);
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-size: clamp(2.2rem, 6vw, 4rem);
  line-height: 1.1;
  text-wrap: balance;
  overflow-wrap: anywhere;
}
.mv-hero-video__lead {
  max-width: 46ch;
  margin: 0;
  color: var(--_mv-hv-muted, #fff);
  font-size: clamp(1.05rem, 2.4vw, 1.2rem);
  overflow-wrap: anywhere;
}
.mv-hero-video__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: 2.75rem;
  padding: .75rem 1.5rem;
  border-radius: var(--radius, 10px);
  background: var(--_mv-hv-act-bg, #fff);
  color: var(--_mv-hv-act-fg, #0a2540);
  font-weight: 700;
  text-decoration: none;
}
.mv-hero-video__cta:hover { background: var(--mv-hero-video-action-hover, var(--_mv-hv-act-bg, #fff)); text-decoration: underline; text-underline-offset: .18em; }
.mv-hero-video__cta:focus-visible { outline: 3px solid var(--_mv-hv-fg, #fff); outline-offset: 3px; }
/* The toggle sits on a moving picture: white ring plus navy halo, visible on any frame. */
.mv-hero-video__toggle:focus-visible {
  outline: 3px solid var(--mv-hero-video-toggle-ring, #fff);
  outline-offset: 2px;
  box-shadow: 0 0 0 7px var(--mv-hero-video-toggle-bg, #0a2540);
}
/* Pause/play (WCAG 2.2.2). Solid, 44px, bottom right of the medium. Hidden
 * until the script has started the video (without video there is nothing to
 * pause). aria-pressed="true" means paused. The icon is drawn, no asset. */
.mv-hero-video__toggle {
  position: absolute;
  right: max(16px, calc((100% - var(--maxw, 1200px)) / 2 + 16px));
  bottom: 16px;
  z-index: 2;
  box-sizing: border-box;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 2px solid var(--mv-hero-video-toggle-ring, #fff);
  border-radius: 999px;
  background: var(--mv-hero-video-toggle-bg, #0a2540);
  color: var(--mv-hero-video-toggle-fg, #fff);
  cursor: pointer;
}
.mv-hero-video__toggle[hidden] { display: none; }
.mv-hero-video__toggle::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 12px;
  height: 14px;
  transform: translate(-50%, -50%);
  border-inline: 4px solid currentColor;
  box-sizing: border-box;
}
.mv-hero-video__toggle[aria-pressed="true"]::before {
  width: 0;
  height: 0;
  transform: translate(-30%, -50%);
  border-inline: 0;
  border-block: 8px solid transparent;
  border-inline-start: 13px solid currentColor;
}
.mv-hero-video__caption {
  position: absolute;
  left: max(24px, calc((100% - var(--maxw, 1200px)) / 2 + 24px));
  right: calc(max(16px, (100% - var(--maxw, 1200px)) / 2 + 16px) + 56px);
  bottom: 20px;
  z-index: 1;
  margin: 0;
  color: var(--_mv-hv-fg, #fff);
  font-size: .9375rem;
  line-height: 1.4;
}
/* The AI mark (tokens.css .ai-badge) lies on the medium. The scrim pseudo-element comes later in
 * the same box and would paint over it (white text measured at 153,164,175, about
 * 3.9:1), so the badge is lifted above the scrim. tokens.css gives it 13px without uppercase,
 * below the 15px floor for functional text, so inside the hero it is 15px. */
.mv-hero-video .mv-hero-video__media > .ai-badge {
  z-index: 1;
  font-size: .9375rem;
}
@media (max-width: 859px) {
  .mv-hero-video .mv-hero-video__media::after {
    background: color-mix(in srgb, var(--_mv-hv-scrim, #0a2540) 78%, transparent);
  }
}

/* float, side-left, side-right: the medium is a card next to or below the
 * copy, the copy sits on the page surface and follows the page theme. */
.mv-hero-video:is([data-layout="float"], [data-layout="side-left"], [data-layout="side-right"]) {
  --_mv-hv-fg: var(--mv-hero-video-text, var(--text, #14202b));
  --_mv-hv-muted: var(--mv-hero-video-muted, var(--text-2, #4a5c68));
  --_mv-hv-eb-bg: var(--mv-hero-video-eyebrow-bg, var(--accent-soft, rgba(12, 110, 109, .12)));
  --_mv-hv-eb-fg: var(--mv-hero-video-eyebrow-text, var(--accent-text, #0c6e6d));
  --_mv-hv-act-bg: var(--mv-hero-video-action-bg, var(--accent, #0c6e6d));
  --_mv-hv-act-fg: var(--mv-hero-video-action-text, var(--on-accent, #fff));
  overflow: visible;
  align-items: center;
  gap: 32px;
  max-width: var(--maxw, 1200px);
  min-height: 0;
  margin-inline: auto;
  padding-block: 40px 56px;
  padding-inline: 24px;
  background: transparent;
}
.mv-hero-video:is([data-layout="float"], [data-layout="side-left"], [data-layout="side-right"]) .mv-hero-video__inner {
  width: auto;
  max-width: none;
  margin-inline: 0;
  padding: 0;
}
.mv-hero-video:is([data-layout="float"], [data-layout="side-left"], [data-layout="side-right"]) .mv-hero-video__media {
  position: relative;
  inset: auto;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-media, var(--radius-lg, 14px));
  box-shadow: var(--shadow-3, 0 8px 24px rgba(10, 37, 64, .14));
}
.mv-hero-video:is([data-layout="float"], [data-layout="side-left"], [data-layout="side-right"]) .mv-hero-video__media::after { content: none; }
.mv-hero-video:is([data-layout="float"], [data-layout="side-left"], [data-layout="side-right"]) .mv-hero-video__caption {
  position: static;
  color: var(--text-3, #5e6f79);
}
.mv-hero-video[data-layout="float"] { justify-items: center; text-align: center; }
.mv-hero-video[data-layout="float"] .mv-hero-video__copy { align-items: center; }
.mv-hero-video[data-layout="float"] .mv-hero-video__lead { margin-inline: auto; }
.mv-hero-video[data-layout="float"] .mv-hero-video__media { width: 100%; max-width: 56rem; }
@media (min-width: 860px) {
  .mv-hero-video:is([data-layout="side-left"], [data-layout="side-right"]) .mv-hero-video__inner {
    position: relative;
    z-index: 1;
    padding: 32px;
    border-radius: var(--radius-lg, 14px);
    background: var(--mv-hero-video-panel, var(--surface, #fff));
    box-shadow: var(--shadow-2, 0 4px 12px rgba(10, 37, 64, .1));
  }
  .mv-hero-video[data-layout="side-left"] { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .mv-hero-video[data-layout="side-left"] .mv-hero-video__media { grid-column: 1; grid-row: 1; }
  .mv-hero-video[data-layout="side-left"] .mv-hero-video__inner { grid-column: 2; grid-row: 1; margin-inline-start: -4rem; }
  .mv-hero-video[data-layout="side-right"] { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  .mv-hero-video[data-layout="side-right"] .mv-hero-video__inner { grid-column: 1; grid-row: 1; margin-inline-end: -4rem; }
  .mv-hero-video[data-layout="side-right"] .mv-hero-video__media { grid-column: 2; grid-row: 1; }
  .mv-hero-video:is([data-layout="side-left"], [data-layout="side-right"]) .mv-hero-video__caption { grid-column: 1 / -1; }
  /* The text panel covers the medium's edge on the side it overlaps: keep the AI badge and the
   * pause button on the other, free side (the badge must never sit under the panel). */
  .mv-hero-video[data-layout="side-left"] .mv-hero-video__media .ai-badge { right: auto; left: 8px; }
  .mv-hero-video[data-layout="side-left"] .mv-hero-video__toggle { right: auto; left: 16px; }
}

/* scroll hero: an image sequence on a canvas, driven by the scroll position
 * (site/assets/js/mv-hero-scrub.js). The section is a tall runway, the stage inside it is
 * sticky and exactly one screen high, so the page scrolls normally and only the picture is
 * held. The runway length is --mv-hero-scroll-length (default 300svh, clamped to 200 to
 * 400svh). Both the fixed stage and the runway apply only once the script has drawn the first
 * frame AND found the copy to fit one screen (data-scrub="on"), and never for reduced motion.
 * In every other state (no script, reduced motion, saved data, a copy taller than the
 * viewport such as a short landscape phone or 400 percent zoom) the stage is just a
 * min-height with visible overflow, so it grows with its content like the full layout and
 * nothing is clipped. The copy sits on the same scrim as the full layout, in the stage.
 * overflow: clip (not hidden) on the section so the sticky stage keeps its scroll parent.
 *
 * Sticky nav: a fork with a sticky header sets --mv-nav-h (its height, default 0px, which
 * changes nothing) on :root or any ancestor of the hero, not on the nav (custom properties
 * inherit downward). The section is then pulled up under the nav by that much, the stage gets
 * the same top padding and the picture box starts below the nav, so the picture fills exactly
 * the visible part of the screen. The copy's top padding shrinks by the same amount (never
 * below 24px) so it still fits one screen. The stage pins at top 0 as before, which is what
 * mv-hero-scrub.js assumes.
 * Stage height while scrubbing: --mv-hero-stage-h, default 100svh (unchanged). Recommended on an
 * iPhone, set together with --mv-nav-h: 100lvh (the large viewport, browser bars collapsed).
 * The Safari bar collapses on the first scroll, and an svh stage then leaves a dark strip at the
 * bottom. Not the default because bottom aligned copy could slide under the bar. The script
 * measures stage.clientHeight, so it follows either value.
 * Curtain (opt-in, data-curtain="on", block prop curtain): the last frame stays while the next
 * section slides over it, see the curtain block below. Its length follows --mv-hero-stage-h too.
 * Beats: caption steps over the sequence, see mv-hero-beats.js. */
.mv-hero-video[data-layout="scroll"] {
  display: block;
  min-height: 0;
  overflow: clip;
  margin-top: calc(-1 * var(--mv-nav-h, 0px));
}
.mv-hero-video[data-layout="scroll"] .mv-hero-video__stage {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: end;
  box-sizing: border-box;
  min-height: 100svh;
  padding-top: var(--mv-nav-h, 0px);
}
.mv-hero-video[data-layout="scroll"] .mv-hero-video__media { top: var(--mv-nav-h, 0px); }
/* 96px is the core top padding of .mv-hero-video__inner: unchanged by default, smaller by the nav offset otherwise. */
.mv-hero-video[data-layout="scroll"] .mv-hero-video__inner { padding-top: max(24px, calc(96px - var(--mv-nav-h, 0px))); }
.mv-hero-video[data-layout="scroll"] .mv-hero-video__poster { object-position: center; }
.mv-hero-video[data-layout="scroll"] .mv-hero-video__canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}
.mv-hero-video[data-layout="scroll"] .mv-hero-video__canvas[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .mv-hero-video[data-layout="scroll"][data-scrub="on"] {
    height: clamp(200svh, var(--mv-hero-scroll-length, 300svh), 400svh);
  }
  .mv-hero-video[data-layout="scroll"][data-scrub="on"] .mv-hero-video__stage {
    position: sticky;
    top: 0;
    height: var(--mv-hero-stage-h, 100svh);
    min-height: 0;
    overflow: hidden;
  }
}

/* Curtain v2 (opt-in via data-curtain="on", needs the scrub to run): geometry only, no animation.
 * The hero gets one stage height more (--mv-hero-stage-h) and gives it back with a negative
 * margin-bottom: the next block starts where it would without the curtain and slides over the last
 * stage height while the sticky stage stands still (compositor sticky, no transform, no timeline).
 * mv-hero-scrub.js and mv-hero-beats.js read that margin as the hold and end the sequence where the
 * overlap begins (only with data-curtain="on"; any other negative margin is not a hold).
 * Later sibling .mv-section/.block get position: relative (zero specificity, no z-index, no
 * transform), so they paint above the positioned hero in tree order. Other elements are NOT lifted
 * (a divider, a bare div): between hero and section they would lie under the stage.
 * Off (plain scroll-away, as before) when: no lifted sibling follows the hero or its .block--hero-video
 * wrapper (the script sets data-curtain-inactive and warns once); the lifted blocks after it are
 * shorter than one stage height (data-curtain-short, set by the script: the hero would hide the footer);
 * keyboard focus is in the hero (:focus-visible: height and margin drop together, page position and
 * following block stay, the stage jumps once: inside the hold it moves up by the part of the hold
 * already scrolled, at most one stage height, and back on blur; the focused control is never under the
 * next block); no :has(), reduced motion, no scrub. The short measure counts only lifted blocks in the
 * flow (display: none, absolute, fixed and floated ones do not count).
 * Page contract for the scroll hero curtain: opaque background on the next block (a
 * transparent one shows the pinned stage, no technical guard possible, check by eye); no overflow
 * hidden/auto/clip on the hero or ANY ancestor (wrapper, main, body with overflow-x: hidden) or the
 * stage stops sticking; a sticky/fixed header needs its own z-index (positioned blocks paint over it);
 * more than one hero per page is untested. overflow: visible on the hero: a browser without
 * overflow: clip would fall back to the core hidden and make the hero a scroll container.
 * No translateY counter-animation: it jumps in iOS Safari. */
@media screen and (prefers-reduced-motion: no-preference) {
  @supports selector(:has(a)) {
    .block--hero-video:has(~ :is(.mv-section, .block)) > .mv-hero-video[data-layout="scroll"][data-curtain="on"][data-scrub="on"]:not([data-curtain-short]):not(:has(:focus-visible)),
    .mv-hero-video[data-layout="scroll"][data-curtain="on"][data-scrub="on"]:has(~ :is(.mv-section, .block)):not([data-curtain-short]):not(:has(:focus-visible)) {
      height: calc(clamp(200svh, var(--mv-hero-scroll-length, 300svh), 400svh) + var(--mv-hero-stage-h, 100svh));
      margin-bottom: calc(-1 * var(--mv-hero-stage-h, 100svh));
      overflow: visible;
    }
    :where(.block--hero-video:has(> .mv-hero-video[data-layout="scroll"][data-curtain="on"][data-scrub="on"]),
      .mv-hero-video[data-layout="scroll"][data-curtain="on"][data-scrub="on"]) ~ :where(.mv-section, .block) { position: relative; }
  }
}

/* Beats (block prop beats, mv-hero-beats.js): caption steps over the scroll sequence. The script sets
 * --mv-hero-video-beat-a (0..1, step opacity) and --mv-hero-video-beats-copy (0..1, the core copy) and
 * adds .mv-hero-video--beats to the hero, only while the scrub runs (data-scrub="on"). Without the
 * class, without JS and with reduced motion the container is display: none and the core copy is the
 * whole hero. A step outside its window is display: none (Safari keeps stale pixels of opacity-only
 * layers); only opacity and transform animate. Overridable: --mv-hero-video-beats-bottom,
 * --mv-hero-video-beats-width, and on phones --mv-hero-video-beats-phone-top (the top edge of the
 * caption, default 42% of the screen). */
.mv-hero-video__beats { display: none; }
.mv-hero-video--beats .mv-hero-video__beats {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}
.mv-hero-video__beat {
  display: none;
  position: absolute;
  left: 50%;
  bottom: var(--mv-hero-video-beats-bottom, clamp(5rem, 14svh, 9rem));
  width: min(var(--mv-hero-video-beats-width, 40rem), calc(100% - 3rem));
  transform: translate(-50%, calc((1 - var(--mv-hero-video-beat-a, 0)) * 14px));
  opacity: var(--mv-hero-video-beat-a, 0);
  text-align: center;
}
.mv-hero-video__beat.is-live { display: block; }
.mv-hero-video__beat-text {
  margin: 0;
  font-family: var(--mv-heading-font, var(--font-heading, inherit));
  font-size: clamp(1.6rem, 4.2vw, 2.8rem);
  line-height: 1.15;
  text-wrap: balance;
  color: var(--_mv-hv-fg, currentcolor);
}
.mv-hero-video__beat-cta { pointer-events: auto; margin-top: 1.4rem; }
@media (max-width: 600px) {
  .mv-hero-video__beat { top: var(--mv-hero-video-beats-phone-top, 42%); bottom: auto; }
}
/* The beat texts are repeated once in the copy for assistive technology (the beats container is aria-hidden);
 * visually hidden, never display: none. */
.mv-hero-video__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
  list-style: none;
}
/* While a beat plays, the core copy steps back. will-change keeps it on one stable layer: without it
 * Safari on iOS could leave the faded copy visible at rest although its computed opacity was 0. */
.mv-hero-video--beats .mv-hero-video__copy {
  opacity: var(--mv-hero-video-beats-copy, 1);
  transform: translateY(calc((1 - var(--mv-hero-video-beats-copy, 1)) * -18px));
  will-change: opacity, transform;
}
/* Faded copy stays in the accessibility tree and tab order; it reappears when it gets focus. */
.mv-hero-video--beats .mv-hero-video__copy.is-gone { pointer-events: none; }
.mv-hero-video--beats .mv-hero-video__copy:focus-within { opacity: 1; transform: none; pointer-events: auto; }
/* A focused copy owns the stage: hide the beats so the two never overlap. */
.mv-hero-video--beats:has(.mv-hero-video__copy:focus-within) .mv-hero-video__beats { visibility: hidden; }
@media (prefers-reduced-motion: reduce) {
  .mv-hero-video--beats .mv-hero-video__beats { display: none; }
}

/* ---------- section head -------------------------------------------------- */
/* No max-width on purpose: text-width rule, the head spans its column.
 * The paragraph rules here and in the story copy exclude .mv-eyebrow, so a pill
 * placed in a head or a story keeps its own colour pair (a plain "p" rule is
 * more specific than the .mv-eyebrow class and would repaint it). */
.mv-section-head { margin: 0 0 32px; }
.mv-section-head h2 {
  margin: 0 0 12px;
  color: var(--mv-head-heading, var(--_mv-text, var(--text, #14202b)));
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-size: clamp(1.5rem, 3.4vw, 1.9rem);
  line-height: 1.2;
}
.mv-section-head p:not(.mv-eyebrow) {
  margin: 0;
  color: var(--mv-head-text, var(--_mv-muted, var(--text-2, #4a5c68)));
  font-size: 1.02rem;
}

/* ---------- story: alternating text and image ----------------------------- */
.mv-story-list {
  display: flex;
  flex-direction: column;
  gap: 56px;
}
.mv-story {
  display: grid;
  grid-template-columns: 1fr;
  gap: 26px;
  align-items: start;
}
.mv-story__copy {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.mv-story__copy :is(h2, h3) {
  margin: 0;
  color: var(--mv-story-heading, var(--_mv-text, var(--text, #14202b)));
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-size: 1.35rem;
}
.mv-story__copy p:not(.mv-eyebrow) {
  margin: 0;
  color: var(--mv-story-text, var(--_mv-muted, var(--text-2, #4a5c68)));
  font-size: 1rem;
  line-height: 1.65;
}
.mv-story__media {
  overflow: hidden;
  border-radius: var(--radius-media, var(--radius-lg, 14px));
  box-shadow: var(--shadow-1, 0 1px 3px rgba(10, 37, 64, .07));
}
.mv-story__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: var(--mv-media-position, center 20%);
}
@media (min-width: 860px) {
  .mv-story { grid-template-columns: 1fr 1fr; }
  .mv-story .mv-story__media { order: 2; }
  .mv-story .mv-story__copy { order: 1; }
  .mv-story--rev .mv-story__media { order: 1; margin-top: 40px; }
  .mv-story--rev .mv-story__copy { order: 2; }
  .mv-story .mv-linkcard { order: 2; }
}

/* ---------- card grid -------------------------------------------- */
/* One column by default, two or three from 700px with data-cols. Rows keep one
 * top edge and equal height (grid stretch). The cards stay .mv-card. */
.mv-grid {
  display: grid;
  gap: var(--mv-grid-gap, 24px);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 700px) {
  .mv-grid[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mv-grid[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Card feet, opt-in: data-feet="aligned" on .mv-grid. Without the attribute the
 * cards in a grid are ordinary blocks, exactly as before. With it, a card whose
 * LAST block holds an action (.mv-button directly or inside a wrapper such as
 * <p> or <div>) becomes a flex column that pushes that block to the foot, so the
 * actions of one row end on one line. Cards without a trailing action stay blocks.
 * Children are stretched; only inline-level ones (img, svg, picture, a, button, span, .mv-eyebrow,
 * canvas, video, iframe, input, select, textarea) and table (no UA margins, so nothing to
 * collapse) keep their natural size (an unsized img stays as it is).
 * Vertical margins no longer collapse in a flex column. Rule: a block keeps the
 * bottom margin of the block above and loses its own top margin, except right
 * after an inline-level sibling (those never collapsed), where it keeps both.
 * Limits of the opt-in:
 *  - several actions must share ONE wrapper; two direct .mv-button children are
 *    pulled apart (only the last one gets the auto margin);
 *  - spacing can differ from an ordinary card by the difference of two margins
 *    (a heading bottom margin larger than the next paragraph top margin, block
 *    wrappers such as section/details/form whose children's margins no longer
 *    collapse through them), typically 3 to 16px for the UA defaults, with no upper
 *    bound promised. Measured outliers: canvas, video, iframe and textarea directly in
 *    the card sit on a line box in an ordinary card (4px baseline gap); as flex items
 *    that gap is gone and the blocks after them move up by 4px; a table with its own
 *    bottom margin (fork CSS) adds it to the next block's top margin (16px more);
 *  - text-align:center does not centre a direct .mv-button, and bare text with
 *    <strong>/<em> becomes stacked flex items (put text in a <p>);
 *  - an hr loses its automatic side margins here, so it spans the card.
 * Needs :has(); a browser without it keeps ordinary blocks. */
.mv-grid > .mv-card > :first-child { margin-top: 0; }
.mv-grid > .mv-card > :last-child { margin-bottom: 0; }
@supports selector(:has(a)) {
  .mv-grid[data-feet="aligned"] > .mv-card:is(:has(> .mv-button:last-child), :has(> :last-child > .mv-button)) {
    display: flex;
    flex-direction: column;
  }
  .mv-grid[data-feet="aligned"] > .mv-card:is(:has(> .mv-button:last-child), :has(> :last-child > .mv-button)) > :is(img, svg, picture, a, button, span, .mv-eyebrow, canvas, video, iframe, input, select, textarea, table) { align-self: flex-start; }
  .mv-grid[data-feet="aligned"] > .mv-card:is(:has(> .mv-button:last-child), :has(> :last-child > .mv-button)) > hr { margin-inline: 0; }
  .mv-grid[data-feet="aligned"] > .mv-card:is(:has(> .mv-button:last-child), :has(> :last-child > .mv-button)) > :not(:last-child):not(:is(img, svg, picture, a, button, span, .mv-eyebrow, canvas, video, iframe, input, select, textarea, table) + *) { margin-top: 0; }
  .mv-grid[data-feet="aligned"] > .mv-card:is(:has(> .mv-button:last-child), :has(> :last-child > .mv-button)) > :last-child { margin-top: auto; }
}

/* ---------- quote ------------------------------------------------- */
/* data-quote="card" (default): put .mv-card on the same element, the card
 * supplies surface and ink. data-quote="large": big centred quote on the
 * section surface. Real quotes only, with name and consent. */
.mv-quote {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
}
.mv-quote blockquote { margin: 0; }
.mv-quote blockquote p {
  margin: 0;
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-size: clamp(1.2rem, 2.4vw, 1.5rem);
  line-height: 1.45;
}
.mv-quote__who {
  margin: 0;
  color: var(--_mv-muted, var(--text-2, #4a5c68));
  font-size: .95rem;
}
.mv-quote[data-quote="large"] { align-items: center; text-align: center; }
.mv-quote[data-quote="large"] blockquote { max-width: 34em; }
.mv-quote[data-quote="large"] blockquote p {
  font-size: clamp(1.5rem, 3.6vw, 2.4rem);
  line-height: 1.3;
  text-wrap: balance;
}
.mv-quote[data-quote="large"] blockquote::before {
  content: "\201C" / "";
  display: block;
  font-family: var(--mv-heading-font, var(--font-heading, Georgia, serif));
  font-size: 4rem;
  line-height: .6;
  opacity: .6;
}

/* ---------- FAQ (native details, no script) ------------------------------- */
.mv-faq-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.mv-faq-list details {
  padding: 4px 18px;
  border: 1px solid var(--mv-faq-border, var(--line-soft, #e6ecef));
  border-radius: var(--radius-md, 10px);
  background: var(--mv-faq-bg, var(--surface, #fff));
}
.mv-faq-list summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 0;
  color: var(--mv-faq-heading, var(--text, #14202b));
  font-size: 1rem;
  font-weight: 700;
  list-style: none;
  cursor: pointer;
}
.mv-faq-list summary::-webkit-details-marker { display: none; }
.mv-faq-list summary:focus-visible {
  outline: 3px solid var(--mv-faq-focus, var(--accent-text, #0c6e6d));
  outline-offset: 2px;
  border-radius: var(--radius-sm, 6px);
}
.mv-faq-list summary::after {
  content: "";
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--mv-faq-chevron, var(--text-3, #5e6f79));
  border-bottom: 2px solid var(--mv-faq-chevron, var(--text-3, #5e6f79));
  transform: rotate(45deg);
  transition: transform 150ms ease;
}
.mv-faq-list details[open] summary::after { transform: rotate(-135deg); }
.mv-faq-list p {
  margin: 0 0 16px;
  color: var(--mv-faq-text, var(--text-2, #4a5c68));
  font-size: 1rem;
  line-height: 1.6;
}
@media (prefers-reduced-motion: reduce) {
  .mv-faq-list summary::after { transition: none; }
}

/* ---------- CTA band ------------------------------------------------------ */
.mv-cta-band {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  padding: 40px 32px;
  border-radius: var(--radius-lg, 14px);
  background: var(--mv-cta-bg, linear-gradient(135deg, var(--accent, #0c6e6d) 0%, var(--accent-2, #0e7c7b) 100%));
  color: var(--mv-cta-text, var(--on-accent, #fff));
  --_mv-action-hover: var(--mv-cta-action-hover, var(--mv-cta-action-bg, var(--on-accent, #fff)));
}
.mv-cta-band h2 {
  margin: 0;
  color: var(--mv-cta-text, var(--on-accent, #fff));
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-size: clamp(1.4rem, 3.2vw, 1.8rem);
}
.mv-cta-band p {
  max-width: 56ch;
  margin: 0;
  color: var(--mv-cta-text-muted, var(--mv-cta-text, var(--on-accent, #fff)));
  font-size: 1.02rem;
}
.mv-cta-band :is(.btn-primary, .mv-button) {
  background: var(--mv-cta-action-bg, var(--on-accent, #fff));
  color: var(--mv-cta-action-text, var(--accent-text, #0c6e6d));
}
.mv-cta-band :is(.btn-primary, .mv-button):hover {
  background: var(--_mv-action-hover, var(--mv-cta-action-bg, var(--on-accent, #fff)));
}
.mv-cta-band :is(a, button):focus-visible {
  outline: 3px solid var(--mv-cta-text, var(--on-accent, #fff));
  outline-offset: 3px;
}
.mv-cta-band__copy {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
@media (min-width: 860px) {
  .mv-cta-band {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: 56px 64px;
  }
  .mv-cta-band__copy { max-width: 60%; }
}

/* CTA band variants: data-cta="centered" stacks and centres the band;
 * no attribute = one row (today). __proof is a single evidence line, only to be
 * filled from a maintained source (never a placeholder claim). */
.mv-cta-band[data-cta="centered"] { align-items: center; text-align: center; }
.mv-cta-band[data-cta="centered"] .mv-cta-band__copy { align-items: center; max-width: none; }
@media (min-width: 860px) {
  .mv-cta-band[data-cta="centered"] { flex-direction: column; justify-content: center; gap: 24px; }
}
.mv-cta-band__proof {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: .95rem;
}
.mv-cta-band[data-cta="centered"] .mv-cta-band__proof { justify-content: center; }
.mv-cta-band__proof::before {
  content: "";
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

/* ---------- media strip (horizontal scroll-snap) -------------------------- */
.mv-media-strip {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-top: 28px;
  padding: 4px 4px 8px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 4px;
}
.mv-media-strip > .mv-media-strip__item {
  flex: 0 0 min(300px, 78%);
  margin-inline: 0;
  scroll-snap-align: start;
}
.mv-media-strip:focus-visible {
  outline: 2px solid var(--mv-strip-focus, var(--accent-text, #0c6e6d));
  outline-offset: 4px;
}
/* "safe" centres only while the items fit; on overflow the row falls back to
 * start alignment, so the first item is never cut off at the left edge (an
 * overflowing flex row with plain center cannot be scrolled back there).
 * A browser without "safe" drops the declaration and would start left-aligned,
 * so the @supports block below centres the row with auto margins on the first
 * and last item instead. Auto margins absorb only positive free space: when the
 * items overflow they collapse to zero and the row starts at the left edge, the
 * same safe behaviour. */
@media (min-width: 1000px) {
  .mv-media-strip { justify-content: safe center; }
  @supports not (justify-content: safe center) {
    .mv-media-strip > :first-child { margin-inline-start: auto; }
    .mv-media-strip > :last-child { margin-inline-end: auto; }
  }
}

/* ---------- caption ------------------------------------------------------- */
.mv-caption {
  max-width: 68ch;
  margin: 18px auto 0;
  color: var(--mv-caption-text, var(--_mv-muted, var(--text-2, #4a5c68)));
  font-size: .9375rem;
  text-align: center;
  text-wrap: balance;
}

/* ---------- link card ----------------------------------------------------- */
.mv-linkcard {
  align-self: start;
  padding: 8px 24px 12px;
  border: 1px solid var(--mv-linkcard-border, var(--line-soft, #e6ecef));
  border-radius: var(--radius-md, 10px);
  background: var(--mv-linkcard-bg, var(--surface, #fff));
}
.mv-linkcard h3 {
  margin: 0;
  padding: 16px 0 12px;
  color: var(--mv-linkcard-heading, var(--text, #14202b));
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-size: 1.15rem;
}
.mv-linkcard__links {
  margin: 0;
  padding: 0;
  list-style: none;
}
.mv-linkcard__links li + li {
  border-top: 1px solid var(--mv-linkcard-border, var(--line-soft, #e6ecef));
}
.mv-linkcard__links a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 44px;
  box-sizing: border-box;
  padding: 14px 0;
  color: var(--mv-linkcard-text, var(--text, #14202b));
  font-weight: 600;
  text-decoration: none;
}
.mv-linkcard__links a::after {
  content: "";
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-top: 2px solid var(--mv-linkcard-accent, var(--accent-text, #0c6e6d));
  border-right: 2px solid var(--mv-linkcard-accent, var(--accent-text, #0c6e6d));
  transform: rotate(45deg);
  transition: transform 150ms cubic-bezier(.23, 1, .32, 1);
}
.mv-linkcard__links a:hover,
.mv-linkcard__links a:focus-visible { color: var(--mv-linkcard-accent, var(--accent-text, #0c6e6d)); }
.mv-linkcard__links a:focus-visible {
  outline: 3px solid var(--mv-linkcard-accent, var(--accent-text, #0c6e6d));
  outline-offset: 2px;
}
@media (hover: hover) and (pointer: fine) {
  .mv-linkcard__links a:hover::after { transform: translateX(3px) rotate(45deg); }
}
@media (prefers-reduced-motion: reduce) {
  .mv-linkcard__links a::after { transition: none; }
}

/* ---------- footer: compact, about 140 px on desktop ---------------------- */
.mv-footer {
  margin-top: 48px;
  border-top: 1px solid var(--mv-footer-border, var(--line, #d3dde1));
  background: var(--mv-footer-bg, var(--bg-2, #f2f5f6));
  color: var(--mv-footer-text, var(--text-2, #4a5c68));
}
.mv-footer__inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  column-gap: 24px;
  row-gap: 0;
  box-sizing: border-box;
  max-width: var(--maxw, 1200px);
  margin: 0 auto;
  padding: 24px 24px 20px;
  text-align: center;
}
.mv-footer__brand,
.mv-footer__copy { flex: 0 0 100%; }
.mv-footer__name {
  margin: 0;
  color: var(--mv-footer-heading, var(--text, #14202b));
  font-family: var(--mv-heading-font, var(--font-heading, system-ui, sans-serif));
  font-weight: 700;
  line-height: 1.4;
}
.mv-footer__tagline {
  margin: 4px 0 0;
  color: var(--mv-footer-text, var(--text-2, #4a5c68));
  font-size: 15px;
  line-height: 1.4;
}
.mv-footer__social a {
  color: var(--mv-footer-link, var(--accent-text, #0c6e6d));
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
}
.mv-footer__social a:hover { text-decoration: underline; }
.mv-footer__badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--mv-footer-text, var(--text-2, #4a5c68));
  font-size: 15px;
  text-decoration: none;
}
.mv-footer__badge img {
  display: block;
  width: 96px;
  height: auto;
}
.mv-footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: 16px;
}
.mv-footer__legal a {
  color: var(--mv-footer-text, var(--text-2, #4a5c68));
  font-size: 15px;
  text-decoration: none;
}
.mv-footer__legal a:hover { color: var(--mv-footer-link, var(--accent-text, #0c6e6d)); }
/* Touch target floor: 44 px for every footer link. */
.mv-footer__social a,
.mv-footer__legal a,
.mv-footer__badge {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.mv-footer :is(a):focus-visible {
  outline: 3px solid var(--mv-footer-link, var(--accent-text, #0c6e6d));
  outline-offset: 2px;
}
.mv-footer__copy {
  margin: 0;
  padding-top: 6px;
  color: var(--mv-footer-muted, var(--text-3, #5e6f79));
  font-size: 15px;
  line-height: 1.4;
}
@media (min-width: 1100px) {
  .mv-footer__inner {
    justify-content: flex-end;
    column-gap: 32px;
    row-gap: 6px;
    text-align: left;
  }
  .mv-footer__brand { flex: 1 1 auto; margin-inline-end: auto; }
  .mv-footer__copy {
    padding-top: 10px;
    border-top: 1px solid var(--mv-footer-border, var(--line, #d3dde1));
    text-align: center;
  }
}
/* Clearance for the fixed consent re-open button (bottom left, 42 px + 16 px)
 * so the last footer lines are never covered on narrow screens. */
@media (max-width: 640px) {
  .mv-footer__inner { padding-bottom: var(--mv-footer-clearance, 88px); }
}

/* ---------- tabs ------------------------------------------- */
/* ARIA tabs with a sliding indicator; behaviour in site/assets/js/mv-tabs.js.
 * Progressive enhancement: without the
 * script every panel stays visible under its own heading and the tab list stays
 * hidden; the script sets data-ready on .mv-tabs, which shows the list and
 * hides the headings (they stay in the accessibility tree). Everything here
 * hangs off .mv-tabs, so a page without it is unchanged.
 *
 * The tab list is a small card-like surface: it reads the card colours of the
 * section's tone (--_mv-card, --_mv-card-text, --_mv-card-border) and the same
 * per-tone card action and focus pair as .mv-card, so it stays readable on
 * accent and inverse sections where the section's own action colour is light.
 * On default and soft the chain ends in the theme tokens (--accent, --on-accent,
 * --accent-text) instead of the card's fixed teal, because there the list
 * surface follows the theme: a fixed teal on the dark surface measured 2.7:1.
 * On accent and inverse the surface is light in both themes, so the fixed pair
 * stays. Segments are equal width (minmax(0, 1fr)) so the indicator moves by
 * transform alone; keep labels short, a long label wraps. */
.mv-tabs > .mv-tabs__list { display: none; }
.mv-section .mv-tabs__list {
  --_mv-action: var(--mv-default-card-action, var(--mv-card-action, var(--accent, #0c6e6d)));
  --_mv-action-text: var(--mv-default-card-action-text, var(--mv-card-action-text, var(--on-accent, #fff)));
  --_mv-focus: var(--mv-default-card-focus, var(--mv-card-focus, var(--accent-text, #0c6e6d)));
}
.mv-section[data-tone="soft"] .mv-tabs__list {
  --_mv-action: var(--mv-soft-card-action, var(--mv-card-action, var(--accent, #0c6e6d)));
  --_mv-action-text: var(--mv-soft-card-action-text, var(--mv-card-action-text, var(--on-accent, #fff)));
  --_mv-focus: var(--mv-soft-card-focus, var(--mv-card-focus, var(--accent-text, #0c6e6d)));
}
.mv-section[data-tone="accent"] .mv-tabs__list {
  --_mv-action: var(--mv-accent-card-action, var(--mv-card-action, #0c6e6d));
  --_mv-action-text: var(--mv-accent-card-action-text, var(--mv-card-action-text, #fff));
  --_mv-focus: var(--mv-accent-card-focus, var(--mv-card-focus, #0c6e6d));
}
.mv-section[data-tone="inverse"] .mv-tabs__list {
  --_mv-action: var(--mv-inverse-card-action, var(--mv-card-action, #0c6e6d));
  --_mv-action-text: var(--mv-inverse-card-action-text, var(--mv-card-action-text, #fff));
  --_mv-focus: var(--mv-inverse-card-focus, var(--mv-card-focus, #0c6e6d));
}
.mv-tabs[data-ready] > .mv-tabs__list {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 0;
  margin-block-start: 0;
  margin-block-end: 28px;
  padding-block: 4px;
  padding-inline: 4px;
  border: 1px solid var(--_mv-card-border, var(--line-strong, #7e8f9a));
  border-radius: 999px;
  background: var(--_mv-card, var(--surface, #fff));
  color: var(--_mv-card-text, var(--text, #14202b));
}
/* The indicator is the list's ::before: no extra element in the markup, nothing
 * for assistive technology to skip. --_mv-tab-i and --_mv-tabs-n are set by the
 * script. */
.mv-tabs[data-ready] > .mv-tabs__list::before {
  content: "";
  position: absolute;
  top: 4px;
  bottom: 4px;
  inset-inline-start: 4px;
  width: calc((100% - 8px) / var(--_mv-tabs-n, 3));
  border-radius: 999px;
  background: var(--_mv-action, var(--accent, #0c6e6d));
  transform: translateX(calc(var(--_mv-tab-i, 0) * 100%));
  pointer-events: none;
}
.mv-tabs[data-ready] > .mv-tabs__list:dir(rtl)::before {
  transform: translateX(calc(var(--_mv-tab-i, 0) * -100%));
}
.mv-tabs__tab {
  position: relative;
  z-index: 1;
  min-height: 2.75rem;
  box-sizing: border-box;
  padding-block: .55rem;
  padding-inline: .75rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: 600;
  line-height: 1.25;
  overflow-wrap: anywhere;
  hyphens: auto;
  cursor: pointer;
}
@media (max-width: 30rem) {
  .mv-tabs__tab { padding-inline: .5rem; }
}
.mv-tabs__tab[aria-selected="true"] { color: var(--_mv-action-text, var(--on-accent, #fff)); }
.mv-tabs__tab:focus-visible {
  outline: 3px solid var(--_mv-focus, var(--accent-text, #0c6e6d));
  outline-offset: 2px;
}
.mv-tabs__panel:focus-visible {
  outline: 3px solid var(--_mv-focus, var(--accent-text, #0c6e6d));
  outline-offset: 4px;
}
.mv-tabs[data-ready] > .mv-tabs__panel[hidden] { display: none; }
.mv-tabs[data-ready] > .mv-tabs__panel > .mv-tabs__heading {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* Without the script the panels are stacked: keep the heading's own spacing and
 * separate the panels. With it only one panel shows and its hidden heading is
 * out of flow, so the first visible block starts flush. */
.mv-tabs:not([data-ready]) > .mv-tabs__panel + .mv-tabs__panel { margin-block-start: 28px; }
.mv-tabs[data-ready] > .mv-tabs__panel > :first-child,
.mv-tabs[data-ready] > .mv-tabs__panel > .mv-tabs__heading + * { margin-block-start: 0; }
.mv-tabs__panel > :last-child { margin-block-end: 0; }

/* Motion: level "subtle" or "reveal" on the section. The
 * indicator glides by transform (movement on screen: ease-in-out, base
 * duration), the label colour fades with ease, and a panel the user switched to
 * enters with a short fade and rise (data-entered is set by the script on a
 * user-driven change only, never on load). The reduced-motion net in
 * sections.css makes all of it instant. */
.mv-section:is([data-motion="subtle"], [data-motion="reveal"]) .mv-tabs[data-ready] > .mv-tabs__list::before {
  transition: transform var(--_mv-motion-base, 250ms) var(--_mv-ease-in-out, ease-in-out);
}
.mv-section:is([data-motion="subtle"], [data-motion="reveal"]) .mv-tabs__tab {
  transition: color var(--_mv-motion-fast, 160ms) ease;
}
.mv-section:is([data-motion="subtle"], [data-motion="reveal"]) .mv-tabs[data-ready] > .mv-tabs__panel[data-entered]:not([hidden]) {
  animation: mv-panel-in var(--_mv-motion-base, 250ms) var(--_mv-ease-out, ease-out) backwards;
}

@media (forced-colors: active) {
  .mv-tabs[data-ready] > .mv-tabs__list::before { background: Highlight; }
  .mv-tabs__tab[aria-selected="true"] { color: HighlightText; }
  .mv-tabs__tab:focus-visible,
  .mv-tabs__panel:focus-visible { outline-color: Highlight; }
}

/* ---------- bento gallery (mv-bento, promoted from the restaurant demo) ----------
 * Asymmetric image grid with captions: <ul class="mv-bento"> of <li class="mv-bento__item"> with an img
 * and an optional <p class="mv-bento__caption">. data-size on an item: wide (4 columns), tall (2 rows),
 * hero (4 columns, 2 rows), none = 2 columns. Six columns on a wide screen, one column of 4:3 tiles on
 * a phone. Plain CSS, inert without the classes. The scroll movement between two layouts is the separate
 * script mv-bento-shift.js (features.bento_shift). */
.mv-bento {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-auto-rows: clamp(9rem, 16vw, 13rem);
  gap: clamp(.6rem, 1.4vw, 1rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.mv-bento__item {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: var(--radius-lg, 14px);
  background: var(--panel-a, var(--bg-2, #f2f5f6));
  grid-column: span 2;
}
.mv-bento__item[data-size="wide"] { grid-column: span 4; }
.mv-bento__item[data-size="tall"] { grid-row: span 2; }
.mv-bento__item[data-size="hero"] { grid-column: span 4; grid-row: span 2; }
.mv-bento__item img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Badge top left, caption bottom right: on narrow tiles both at the bottom overlapped. */
.mv-bento__item .ai-badge { top: 10px; bottom: auto; }
.mv-bento__caption {
  position: absolute;
  right: .6rem;
  bottom: .6rem;
  max-width: calc(100% - 1.2rem);
  margin: 0;
  padding: .3rem .7rem;
  border-radius: 999px;
  font-size: .95rem;
  font-weight: 600;
  background: var(--surface, #fff);
  color: var(--text, #14202b);
}
@media (max-width: 720px) {
  /* One column, every picture in its own 4:3 frame, nothing cut to a sliver. */
  .mv-bento { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; }
  .mv-bento .mv-bento__item,
  .mv-bento .mv-bento__item[data-size] { grid-column: auto; grid-row: auto; aspect-ratio: 4 / 3; }
}
/* mv-bento-shift.js measures the grid, then lays the tiles out absolutely (class set by the script only). */
.mv-bento.is-shift { display: block; position: relative; }
.mv-bento.is-shift > .mv-bento__item { position: absolute; margin: 0; }

/* ---------- opening hours (mv-hours) ---------------------------------------------
 * A definition list, one day (or day range) per row: <dl class="mv-hours"><dt>Mo bis Fr</dt>
 * <dd><span>12:00 bis 15:00 Uhr</span><span>18:30 bis 23:00 Uhr</span></dd> ...</dl>. One time window per
 * line (a span each), a thin rule per day, times aligned right; on narrow columns the day sits above its
 * times. The framework keeps no opening hours in site.config.json, so the markup is the fork's. */
.mv-hours {
  display: grid;
  /* A long label wraps at 60% instead of pushing the value off a phone screen. */
  grid-template-columns: fit-content(60%) minmax(0, 1fr);
  gap: 0;
  margin: 0;
}
.mv-hours dt { font-weight: 700; color: var(--_mv-text, var(--text, #14202b)); }
.mv-hours dd {
  margin: 0;
  padding-left: 1.5rem;
  text-align: right;
  color: var(--_mv-muted, var(--text-2, #4a5a68));
  font-variant-numeric: tabular-nums;
}
.mv-hours dt,
.mv-hours dd { padding-top: .65rem; padding-bottom: .65rem; border-top: 1px solid var(--line, rgba(0, 0, 0, .16)); border-top: 1px solid color-mix(in srgb, currentColor 16%, transparent); }
.mv-hours dt:first-of-type,
.mv-hours dt:first-of-type + dd { border-top: 0; padding-top: 0; }
.mv-hours dd span { display: block; white-space: nowrap; }
@media (max-width: 520px) {
  .mv-hours { grid-template-columns: minmax(0, 1fr); }
  .mv-hours dd { text-align: left; padding-left: 0; padding-top: 0; border-top: 0; }
}

/* ---------- A to B on scroll (mv-scroll-ab, script mv-scroll-ab.js, features.scroll_ab) ----
 * <figure class="mv-scroll-ab"> holds the end state B as its normal <img> and the start state A on top
 * (<img class="mv-scroll-ab__a" alt="" aria-hidden="true">). While the figure scrolls up through the
 * viewport the script fades A out; with data-frames="<base>" data-count="N" data-ext="jpg" it plays an
 * image sequence on a canvas instead. Without the script, and with reduced motion, B is what you see:
 * A stays at opacity 0 until the script has taken over (.is-ab). */
.mv-scroll-ab { position: relative; margin: 0; overflow: hidden; }
.mv-scroll-ab > img { display: block; width: 100%; height: auto; }
.mv-scroll-ab .mv-scroll-ab__a { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; }
.mv-scroll-ab.is-ab .mv-scroll-ab__a { opacity: 1; }
.mv-scroll-ab .mv-scroll-ab__canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; opacity: 0; }
.mv-scroll-ab.has-frames .mv-scroll-ab__canvas { opacity: 1; }
.mv-scroll-ab .ai-badge { z-index: 2; }
@media (prefers-reduced-motion: reduce) {
  .mv-scroll-ab .mv-scroll-ab__a, .mv-scroll-ab .mv-scroll-ab__canvas { display: none; }
}

/* ---------- forced colors ------------------------------------------------- */
@media (forced-colors: active) {
  .mv-eyebrow,
  .mv-cta-band,
  .mv-hero__media { border: 1px solid CanvasText; }
  .mv-hero-video__eyebrow,
  .mv-hero-video__cta { border: 1px solid CanvasText; }
  .mv-hero-video__toggle { border-color: ButtonText; background: ButtonFace; color: ButtonText; }
  .mv-hero { box-shadow: none; clip-path: none; }
  .mv-faq-list summary::after { border-color: ButtonText; }
  .mv-linkcard__links a::after { border-color: LinkText; }
  .mv-media-strip:focus-visible,
  .mv-faq-list summary:focus-visible,
  .mv-linkcard__links a:focus-visible,
  .mv-footer a:focus-visible { outline-color: Highlight; }
}
