/* ============================================================================
   site-chrome.css — the shared site navbar + footer.

   SINGLE SOURCE OF TRUTH for the public-site chrome. The markup that goes with
   it lives in partials/site-nav.html and partials/site-footer.html and is
   stamped into every public page by tests/site-chrome-sync.mjs. Do not
   hand-edit the chrome inside a page: edit the partial, then run
       node tests/site-chrome-sync.mjs --write
   (the no-arg form is a drift check that exits 1, same idiom as
   tests/login-inline-sync.mjs).

   WHY EVERYTHING IS NAMESPACED `.mv-*`
   The chrome was previously copy-pasted into ~23 pages, each of which grew its
   own inline <style> for `.topnav` / `.ftgrid` / `.wrap` / `.logo` / `.legal`,
   plus 27-57 bare `footer ... {}` rules per page. A shared stylesheet using
   those same names would lose every specificity fight, because a page's inline
   <style> block comes after this <link> in source order. So the shared chrome
   uses class names nothing else in the codebase uses, and every root element
   carries a class (`.mv-topnav`, `.mv-site-footer`) so that a bare `header {}`
   or `footer {}` rule on the page — specificity (0,0,1) — cannot beat it.
   The pages' now-orphaned chrome CSS is left in place deliberately: it no
   longer matches anything, and deleting it across 23 divergent files is a
   separate, individually-verifiable change.

   THEMING
   tokens.css makes LIGHT the :root default and dark opt-in via
   [data-theme="dark"]. The old homepage chrome was authored the other way
   round (dark first, with a [data-theme="light"] override block), which only
   worked because index.html/pricing.html explicitly set data-theme="light".
   The free-tools pages do not — they only ever set the attribute for dark. So
   this file is written light-first with a [data-theme="dark"] override, which
   renders correctly whether or not the light attribute is present.
   ============================================================================ */

/* ---------------------------------------------------------------- navbar -- */

.mv-topnav {
  --mv-nav-bg: var(--panel);
  --mv-nav-line: var(--border);
  position: sticky;
  top: 0;
  z-index: 50;
  padding: 20px 0;
  background: var(--mv-nav-bg);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  border-bottom: 1px solid var(--mv-nav-line);
}

[data-theme="dark"] .mv-topnav {
  --mv-nav-bg: rgba(5, 2, 14, 0.78);
  --mv-nav-line: var(--hairline);
}

.mv-nav-row {
  max-width: 1340px;
  margin: 0 auto;
  padding: 0 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Brand wordmark. Previously six inline style="" attributes on every page. */
.mv-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
}

.mv-brand img {
  height: 32px;
  width: auto;
  display: block;
  flex: none;
  object-fit: contain;
}

.mv-brand-word {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 21px;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--text);
  line-height: 1;
  white-space: nowrap;
}

/* The second half of the wordmark is the brand accent and stays pink in both
   themes — it reads on either background. */
.mv-brand-word b {
  color: var(--magenta-bright);
  font-weight: inherit;
}

.mv-nav-mid {
  display: flex;
  /* 34, not 36. A dropdown trigger is 15px wider than the plain link it
     replaced (the caret plus its 5px gap), and at 1280 - the most common laptop
     width - the bar had only 2px of headroom before fitNav() decides the links
     are colliding with the logo and hands off to the hamburger. Measured: 36px
     gives an 11px side gap at 1280 against a 16px threshold, so the full navbar
     disappeared at 1280 the moment Solutions gained a submenu.
     SEVEN gaps, not eight: .mv-nav-mid has ten children but the two
     .mv-nav-mobile-only links are display:none up here and generate no flex
     gap. So this recovers 14px of the 15, and the bar ends up 1px wider than
     before rather than identical - side gap at 1280 goes 18.5 to 18.0, and the
     width at which the bar collapses moves 1276 to 1277. Measured, both ways:
     at gap:36 with the caret it collapses below 1291, which is what made the
     navbar vanish at 1280. */
  gap: 34px;
  align-items: center;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-2);
}

/* Colour + text-decoration are stated explicitly rather than inherited: pages
   carry bare `header a {}` / `nav a {}` rules that would otherwise leak in. */
.mv-topnav .mv-nav-mid a {
  color: var(--text-2);
  text-decoration: none;
  transition: color 0.2s;
}

/* ---- hover + current-page state ------------------------------------------
   Hover and the current page share one treatment: brand purple plus an
   underline. The difference is only that the current page's underline is
   always drawn, so the nav says where you are without a second visual
   language for it.

   The rule is a pseudo-element, not text-decoration: an underline that can
   grow from the centre needs something to transform, and text-decoration
   cannot be animated. It sits below the baseline so descenders are not
   crossed, and takes currentColor so it can never drift from the text.

   Scoped to `> a` plus the dropdown trigger on purpose — `.mv-nav-mid a`
   would also match the eleven links inside the free-tools menu, and
   underlining those on hover would be noise. */
.mv-nav-mid > a,
.mv-nav-dd-trigger {
  position: relative;
}

.mv-nav-mid > a::after,
.mv-nav-dd-trigger::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -7px;
  height: 2px;
  border-radius: var(--radius-xs);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 0.2s ease;
}

/* Scoped under .mv-topnav to win a specificity fight, not for tidiness.
   index.html carries `[data-theme="light"] header a:hover { color: var(--text) }`
   at (0,2,2), which beats a bare `.mv-nav-mid > a:hover` at (0,2,1) — so the
   hover colour silently stayed near-black, and the underline went with it
   because it draws in currentColor. Under .mv-topnav these are (0,3,1) and win.
   Same trap that caught .mv-cta-mini earlier; that page rule outranks anything
   in this file that is not scoped. */
.mv-topnav .mv-nav-mid > a:hover,
.mv-topnav .mv-nav-mid > a:focus-visible,
.mv-topnav .mv-nav-mid > a.is-active,
.mv-topnav .mv-nav-dd:hover .mv-nav-dd-trigger,
.mv-topnav .mv-nav-dd:focus-within .mv-nav-dd-trigger,
.mv-topnav .mv-nav-dd-trigger.is-active {
  color: var(--purple-bright);
}

.mv-nav-mid > a:hover::after,
.mv-nav-mid > a:focus-visible::after,
.mv-nav-mid > a.is-active::after,
.mv-nav-dd:hover .mv-nav-dd-trigger::after,
.mv-nav-dd:focus-within .mv-nav-dd-trigger::after,
.mv-nav-dd-trigger.is-active::after {
  transform: scaleX(1);
}

/* On a free-tools page the sync script marks the item INSIDE the dropdown, so
   the trigger itself carries no .is-active. Without this the navbar would show
   no current-page state at all on eleven of the twenty-one pages. */
.mv-topnav .mv-nav-dd:has(.mv-nav-dd-menu .is-active) .mv-nav-dd-trigger {
  color: var(--purple-bright);
}

.mv-nav-dd:has(.mv-nav-dd-menu .is-active) .mv-nav-dd-trigger::after {
  transform: scaleX(1);
}

/* The dropdown's own items keep the panel's hover treatment — a background
   tint — rather than growing underlines inside the menu. */
.mv-topnav .mv-nav-dd-menu a.is-active {
  color: var(--text);
  background: var(--border);
}

@media (prefers-reduced-motion: reduce) {
  .mv-nav-mid > a::after,
  .mv-nav-dd-trigger::after {
    transition: none;
  }
}

.mv-nav-mid blog-lang-switcher {
  display: inline-flex;
  align-items: center;
}

.mv-nav-mid .mv-nav-mobile-only {
  display: none;
}

.mv-nav-right {
  display: flex;
  gap: 14px;
  align-items: center;
}

/* ------------------------------------------------ language switcher ------ */
/* The widget's markup and behaviour come from js/mv-i18n-runtime.js, which
   injects its own baseline CSS. These rules restyle it to match the pill on
   /login — a neutral rounded pill rather than the runtime's purple-tinted
   rectangle — so the two switchers on the site read as one component.

   Every rule below is scoped under .mv-topnav on purpose. The runtime appends
   its <style id="mv-langw-css"> to <head> at RUNTIME, i.e. after this file's
   <link>, so source order favours it. These win at (0,2,0) against its (0,1,0)
   instead — no !important, and the widget keeps its own look anywhere outside
   the navbar. Editing the runtime was avoided deliberately: it is shared with
   the other lane's i18n work. */
.mv-topnav .mv-langw-btn {
  gap: 6px;
  padding: 5px 10px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--bg-2);
  color: var(--text);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1;
}

[data-theme="dark"] .mv-topnav .mv-langw-btn {
  background: rgba(255, 255, 255, 0.04);
}

.mv-topnav .mv-langw-btn:hover {
  background: var(--border);
  border-color: var(--border-strong);
}

/* 2px, matching /login, NOT a radius token. The scale starts at
   --radius-xs: 6px, which on an 18x13 flag rounds the corners so hard the
   thing reads as a lozenge rather than a flag — that is what the tokenised
   version of this rule looked like. The radius scale governs UI surfaces;
   this is a content image whose own edges are being softened, so it takes
   the smallest value that still avoids a hard corner. */
.mv-topnav .mv-langw-flag {
  width: 18px;
  height: 13px;
  border-radius: 2px;
}

/* The caret is decorative next to a two-letter code; shrink it to match. */
.mv-topnav .mv-langw-btn svg {
  width: 9px;
  height: 5px;
  opacity: 0.65;
}

/* The mobile CTA lives in the collapsible menu; hidden until the menu opens.
   See the 560px block for what swaps. */
.mv-nav-cta-mobile {
  display: none;
}

/* The menu matches /login's: a panel surface, not the runtime's default. */
.mv-topnav .mv-langw-menu {
  min-width: 200px;
  padding: 8px;
  border-radius: var(--radius-lg);
  background: var(--panel);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
}

.mv-topnav .mv-langw-opt {
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  color: var(--text-2);
}

.mv-topnav .mv-langw-opt:hover,
.mv-topnav .mv-langw-opt.active {
  background: var(--border);
  color: var(--text);
}

.mv-topnav .mv-nav-link {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-2);
  text-decoration: none;
}

.mv-topnav .mv-nav-link:hover {
  color: var(--text);
}

/* Scoped under .mv-topnav purely to win a specificity fight. index.html carries
   `[data-theme="light"] header a { color: var(--text-2) }` at (0,1,2), which
   outranks a bare `.mv-cta-mini` at (0,1,0) and painted the label dark on the
   purple gradient. --on-brand is deliberately not themed: the pill stays a brand
   surface in both themes, so its text must stay light in both. */
.mv-topnav .mv-cta-mini {
  padding: 11px 22px;
  background: var(--gradient);
  color: var(--on-brand);
  border-radius: var(--radius-pill);
  font-weight: 700;
  font-size: 13px;
  text-decoration: none;
  
  transition: transform 0.2s;
}

.mv-topnav .mv-cta-mini:hover,
.mv-topnav .mv-cta-mini:focus {
  color: var(--on-brand);
  transform: translateY(-1px);
}

.mv-nav-toggle {
  display: none;
  width: 42px;
  height: 42px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg-2);
  cursor: pointer;
  padding: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

.mv-nav-toggle span {
  display: block;
  width: 18px;
  height: 2px;
  background: var(--text);
  border-radius: var(--radius-xs);
  transition:
    transform 0.25s ease,
    opacity 0.2s ease;
}

.mv-topnav.is-open .mv-nav-toggle span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}

.mv-topnav.is-open .mv-nav-toggle span:nth-child(2) {
  opacity: 0;
}

.mv-topnav.is-open .mv-nav-toggle span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* ------------------------------------------------- free-tools dropdown --- */

.mv-nav-dd {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.mv-nav-dd-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  transition: color 0.2s;
}

.mv-nav-dd-caret {
  transition: transform 0.2s ease;
  opacity: 0.7;
}

.mv-nav-dd:hover .mv-nav-dd-caret,
.mv-nav-dd:focus-within .mv-nav-dd-caret {
  transform: rotate(180deg);
}

.mv-nav-dd:hover .mv-nav-dd-trigger,
.mv-nav-dd:focus-within .mv-nav-dd-trigger {
  color: var(--purple-bright);
}

.mv-nav-dd-menu {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  min-width: 210px;
  padding: 8px;
  margin-top: 10px;
  background: var(--panel);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 0.2s ease,
    transform 0.2s ease,
    visibility 0.2s;
  z-index: 60;
}

[data-theme="dark"] .mv-nav-dd-menu {
  background: rgba(10, 4, 32, 0.96);
}

/* Hover bridge so the menu doesn't close in the gap under the trigger. */
.mv-nav-dd::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 18px;
}

.mv-nav-dd:hover .mv-nav-dd-menu,
.mv-nav-dd:focus-within .mv-nav-dd-menu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}

.mv-topnav .mv-nav-dd-menu a {
  display: block;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
  transition:
    background 0.15s,
    color 0.15s;
}

.mv-topnav .mv-nav-dd-menu a:hover,
.mv-topnav .mv-nav-dd-menu a:focus {
  background: var(--border);
  color: var(--text);
}

/* ---------------------------------------------------------------- footer -- */

.mv-site-footer {
  background: var(--bg-2);
  padding: 80px 32px 36px;
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: 13.5px;
  position: relative;
  z-index: 1;
}

[data-theme="dark"] .mv-site-footer {
  background: linear-gradient(180deg, var(--bg-2), var(--bg));
}

.mv-ftwrap {
  max-width: 1340px;
  margin: 0 auto;
}

.mv-ftgrid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 44px;
  border-bottom: 1px solid var(--hairline);
}

.mv-ftcol h4 {
  color: var(--text);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 13px;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin-bottom: 18px;
}

.mv-ftcol ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 11px;
  padding: 0;
  margin: 0;
}

.mv-ftcol a {
  color: var(--muted);
  font-size: 13.5px;
  text-decoration: none;
  transition: color 0.15s ease;
}

.mv-ftcol a:hover {
  color: var(--text);
}

.mv-ftlogo {
  color: var(--text);
  margin-bottom: 14px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
}

.mv-ftlogo img {
  height: 30px;
  width: auto;
  display: block;
  flex: none;
  object-fit: contain;
}

.mv-ftbrand p {
  font-size: 13.5px;
  color: var(--muted);
  line-height: 1.65;
  max-width: 290px;
  margin-bottom: 22px;
}

.mv-ftcontact {
  list-style: none;
  margin: 22px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
  font-size: 13px;
  color: var(--text-2);
}

.mv-ftcontact li {
  display: flex;
  gap: 10px;
  align-items: baseline;
}

/* WCAG 2.5.8 asks for 24px on a tap target. These two measured 21px on a
   390px viewport. They are not inline-in-text, so the exemption does not apply.
   inline-flex rather than a height, so the row keeps its baseline alignment. */
.mv-ftcontact a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

.mv-ftcontact .mv-lbl {
  flex-shrink: 0;
  min-width: 52px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* --muted-soft is the faintest tier and measured 3.37:1 on the light
     background, under the 4.5 needed for 10px text. --muted is the token meant
     for small labels and clears it. Dark is unaffected: both tiers pass there. */
  color: var(--muted);
  font-weight: 700;
}

.mv-ftcontact a {
  color: var(--text-2);
  text-decoration: none;
  transition: color 0.15s ease;
}

.mv-ftcontact a:hover {
  color: var(--text);
}

.mv-ftbottom {
  padding-top: 28px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
  font-size: 12.5px;
  color: var(--muted);
}

.mv-ftbottom-left {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.mv-ftlegal {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
}

.mv-ftlegal a {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.15s ease;
}

.mv-ftlegal a:hover {
  color: var(--text);
}

.mv-ftbadge {
  display: inline-flex;
  /* 22px measured on mobile. It is a real link to the status page, not inline
     in a sentence, so WCAG 2.5.8 applies. */
  min-height: 24px;
  align-items: center;
  gap: 7px;
  padding: 4px 10px;
  background: var(--green-tint);
  color: var(--green);
  border-radius: var(--radius-xs);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  /* The badge doubles as the status-page link. Keep it looking like the pill,
     not a default underlined anchor, with a faint hover lift. */
  text-decoration: none;
  transition: background 0.15s ease;
}

/* Hover brightens to the panel rather than to --border-strong: that is a
   neutral dark tint, and green on it came to 4.03:1, so hovering the badge used
   to drop it back below AA. --panel passes in both themes (5.15 light, and it
   is near-black in dark). */
a.mv-ftbadge:hover {
  background: var(--panel);
}

/* The status dot breathes and emits a soft ring, so "All systems operational"
   reads as a live signal rather than a static label. The ring is drawn with
   box-shadow rather than a second pseudo-element (::before is already the dot
   and ::after is unused but would need absolute positioning against a padded,
   inline-flex badge). Colour comes from --green via color-mix, so the ring
   follows the theme instead of hardcoding an rgba. */
.mv-ftbadge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--green);
  animation: mv-ftbadge-pulse 2.4s ease-out infinite;
}

@keyframes mv-ftbadge-pulse {
  0% {
    opacity: 1;
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--green) 55%, transparent);
  }
  70% {
    opacity: 0.72;
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--green) 0%, transparent);
  }
  100% {
    opacity: 1;
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--green) 0%, transparent);
  }
}

/* A pulsing dot is decorative; for anyone who has asked the OS to reduce
   motion it is just a distraction next to a static status label. */
@media (prefers-reduced-motion: reduce) {
  .mv-ftbadge::before {
    animation: none;
  }
}

/* ------------------------------------------------------------ responsive -- */

/* The desktop nav is seven items wide (Home, About, Platform, Pricing, Blog,
   Free tools, Contact). At 36px gaps that row needs ~1162px of viewport before
   it starts wrapping onto a second line, and the hamburger did not take over
   until 980px — so the bar silently broke across the whole 980-1160 band. It
   was already wrapping at 1024px with six items; Contact only widened the
   window.

   Two changes close it. Tightening the gap to 24px below 1180 buys back 72px
   and carries the single line down to ~1090px, and the collapse threshold
   below moves to 1100px so the hamburger takes over before the wrap can start.
   Measured, not guessed: item widths total 346px, brand 186px, right cluster
   270px, row padding 112px. */
@media (max-width: 1180px) {
  .mv-nav-mid {
    gap: 24px;
  }
}

/* NAV collapse. This was one 980px block covering the navbar AND the footer
   grid; raising it wholesale would have moved the footer's two-column
   breakpoint with it. The footer keeps its own 980px block further down. */
@media (max-width: 1100px) {
  .mv-nav-row {
    padding: 0 22px;
  }

  .mv-nav-mid {
    display: none;
  }

  .mv-nav-toggle {
    display: flex;
  }
}

/* Open (hamburger) dropdown styling. Kept OUT of the collapse media query on
   purpose: it only takes effect when .is-open is set, which the JS sets only
   while .mv-nav-toggle is shown (i.e. while collapsed), so it never touches the
   desktop bar — and this way the EARLIER non-English collapse (see the
   localization block at the end of this file) styles its open menu correctly too. */
  .mv-topnav.is-open .mv-nav-mid {
    display: flex;
    flex-direction: column;
    gap: 0;
    align-items: stretch;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--mv-nav-bg);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    border-bottom: 1px solid var(--mv-nav-line);
    padding: 6px 22px 14px;
    box-shadow: var(--shadow-lg);
    /* Expanding the free-tools submenu adds eleven rows. The panel is
       absolutely positioned under a sticky header, so anything past the
       viewport would simply be unreachable — it does not scroll with the page.
       Scroll it internally instead. */
    max-height: calc(100vh - 90px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  [data-theme="dark"] .mv-topnav.is-open .mv-nav-mid {
    background: rgba(5, 2, 14, 0.97);
  }

  .mv-topnav.is-open .mv-nav-mid a {
    padding: 14px 2px;
    font-size: 16px;
    color: var(--text);
    border-bottom: 1px solid var(--mv-nav-line);
  }

  .mv-topnav.is-open .mv-nav-mid a:last-child {
    border-bottom: none;
  }

  /* No sliding underline in the collapsed menu: these rows already carry a
     border-bottom, and a second 2px rule 7px under the text would read as a
     doubled divider. The purple still marks the current page. */
  .mv-topnav.is-open .mv-nav-mid > a::after,
  .mv-topnav.is-open .mv-nav-mid .mv-nav-dd-trigger::after {
    display: none;
  }

  .mv-topnav.is-open .mv-nav-mid .mv-nav-mobile-only {
    display: block;
    color: var(--text);
  }

/* FOOTER — deliberately still 980px. The navbar collapses earlier (1100px)
   because seven items stop fitting; the footer grid has no such constraint and
   moving it would reflow every page for no reason. */
@media (max-width: 980px) {
  .mv-ftgrid {
    grid-template-columns: 1fr 1fr;
    gap: 30px;
  }

  .mv-ftbrand {
    grid-column: 1 / -1;
  }

  .mv-ftbottom {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }

  .mv-site-footer {
    padding: 64px 22px 32px;
  }
}

@media (max-width: 860px) {
  .mv-nav-dd {
    display: block;
  }

  .mv-nav-dd::after {
    display: none;
  }

  /* The caret STAYS visible here — it is the only affordance telling you this
     row expands. It was hidden while the submenu was permanently open. */
  .mv-nav-dd-caret {
    display: inline-block;
    margin-left: auto;
  }

  .mv-nav-dd.is-open .mv-nav-dd-caret {
    transform: rotate(180deg);
  }

  .mv-nav-dd-trigger {
    width: 100%;
    justify-content: space-between;
  }

  /* Collapsed by default. It used to be forced open here — static, opaque and
     visible — which listed all eleven tools inline. That made the menu ~734px
     tall and pushed "Sign in" to 708px and "Start free" to 752px, below the
     fold of a 390x844 phone once browser chrome is counted: the reason both
     buttons looked missing. Tapping the row toggles .is-open (see
     js/mv-site-chrome.js). */
  .mv-nav-dd-menu {
    position: static;
    transform: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    min-width: 0;
    margin: 0;
    padding: 0 0 0 14px;
    background: none;
    border: none;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    display: none;
  }

  .mv-nav-dd.is-open .mv-nav-dd-menu {
    display: block;
    margin: 4px 0 8px;
    padding: 4px 0 4px 14px;
  }

  /* The hover/focus rules must be neutralised here, not just the base one.
     `.mv-nav-dd:hover .mv-nav-dd-menu` and its :focus-within twin are (0,2,0)
     and beat the (0,1,0) `transform: none` above — so tapping the row focused
     it, translateX(-50%) came back, and the submenu slid half its width off
     the left of the screen while the panel stayed put. Centring is only ever
     wanted for the absolutely-positioned desktop dropdown. */
  .mv-nav-dd:hover .mv-nav-dd-menu,
  .mv-nav-dd:focus-within .mv-nav-dd-menu {
    transform: none;
    left: auto;
  }

  [data-theme="dark"] .mv-nav-dd-menu {
    background: none;
  }
}

@media (max-width: 560px) {
  .mv-nav-row {
    padding: 0 18px;
  }

  .mv-topnav {
    padding: 15px 0;
  }

  .mv-brand-word {
    font-size: 18px;
  }

  .mv-brand {
    gap: 9px;
  }

  .mv-brand img {
    height: 26px;
  }

  .mv-nav-right {
    gap: 8px;
  }

  .mv-nav-link {
    display: none;
  }

  /* On a 390px viewport the bar wanted about 421px of content: brand ~142,
     CTA ~100, switcher ~77, toggle 42, plus gaps and 36 of row padding. Both
     buttons now move into the menu, leaving the bar as brand + switcher +
     toggle — about 317px, so it fits with room to spare, and the switcher
     stays where it can be seen rather than being buried behind the toggle. */
  .mv-cta-mini {
    display: none;
  }

  .mv-topnav.is-open .mv-nav-mid .mv-nav-cta-mobile {
    display: block;
    margin-top: 14px;
    padding: 13px 18px;
    border-radius: var(--radius-pill);
    background: var(--gradient);
    color: var(--on-brand);
    text-align: center;
    font-weight: 700;
    font-size: 14px;
    border-bottom: none;
  }

  /* The brand is the only flexible item left; let it shrink before it can
     push the row into an overlap, and clip rather than overlay if it must. */
  .mv-brand {
    min-width: 0;
  }

  .mv-brand-word {
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .mv-ftgrid {
    grid-template-columns: 1fr;
    gap: 26px;
  }

  .mv-site-footer {
    padding: 52px 18px 28px;
  }
}

@media (max-width: 400px) {
  .mv-nav-row {
    padding: 0 16px;
  }
}

/* ============================================================================
   Localization safety net (site chrome + public pages)
   ----------------------------------------------------------------------------
   Every non-English translation is longer than the English these components
   were sized for (German has very long compound words; Russian runs ~30% longer).
   The rules below let longer text WRAP and FIT rather than overflow, clip, or
   collide. They are additive and scoped so the English layout is unchanged. The
   i18n runtime stamps <html lang="..">, which drives the :lang() scoping.
   ============================================================================ */

/* Over-long words (German compounds, URLs) break to the next line instead of
   pushing the page wider than the viewport. overflow-wrap is inherited, so one
   rule on body covers every public page's content; it only engages when a word
   would otherwise overflow, so normal text is untouched. */
/* The site font, as a floor rather than a per-page chore.

   tokens.css DEFINES --font but nothing applied it: each page was expected to
   set it on its own root class. Nine pages never did - the five platform
   pages, the two solutions subpages and the two alternatives pages - so they
   downloaded Inter and rendered in the browser default serif, taking the
   navbar inside them with it, because the nav inherits its font from the page.

   Element specificity (0,0,1) on purpose. Any page setting its own base font
   does it through a class on this same element at (0,1,0) and still wins; this
   only reaches pages that set nothing at all. */
body {
  font-family: var(--font); /* base — a page class overrides this */
  overflow-wrap: break-word;
}

/* The horizontal nav bar is sized for English; every other language is longer,
   so tighten the middle links (and the right cluster's gap) so German/Russian
   fit the same row instead of colliding with the logo and the CTA. */
html:not(:lang(en)) .mv-nav-mid { gap: 20px; font-size: 13px; }
html:not(:lang(en)) .mv-nav-right { gap: 10px; }

/* In the horizontal bar the links stay on one line, so "Kostenlose Tools" /
   "Бесплатные инструменты" never break mid-bar and the JS fit check below reads a
   clean width. The open mobile menu lets them wrap normally. */
.mv-topnav .mv-nav-mid > a,
.mv-topnav .mv-nav-dd-trigger { white-space: nowrap; }
.mv-topnav.is-open .mv-nav-mid > a,
.mv-topnav.is-open .mv-nav-dd-trigger { white-space: normal; }
/* Submenu items too. They were left out, which no English label was long enough
   to expose: measured at 320px with the Solutions submenu open, the Spanish and
   Russian names of these two pages run 340px and 311px inside a 262px box and
   the panel scrolls sideways to read them. */
.mv-topnav.is-open .mv-nav-dd-menu a { white-space: normal; }

/* Language-agnostic collapse. js/mv-site-chrome.js adds .mv-nav-cramped whenever
   the horizontal links do not actually fit the bar — for ANY language at ANY
   width, so German collapses later than Russian, each exactly when it needs to,
   with no per-language breakpoints to keep in sync. The 1100px media query above
   stays as the no-JS fallback; the open-menu styling is unconditional, so this
   collapse shows the same working dropdown. */
.mv-topnav.mv-nav-cramped .mv-nav-mid { display: none; }
.mv-topnav.mv-nav-cramped .mv-nav-toggle { display: flex; }
.mv-topnav.mv-nav-cramped .mv-nav-row { padding: 0 22px; }
