/* ══════════════════════════════════════════════════════════════════════════
   assets/public-shell.css
   Mark VII Car Wash Academy — PUBLIC design system.

   Written from scratch (session 19) to replace assets/public.css. The old
   sheet had accumulated three eras of rules — markvii.net-era motifs, a
   half-ported WashTec layer, and a trailing "rescale" block that silently
   overrode the layer above it. Symptoms Daniel reported (nav anchors blue
   while nav buttons were white, search pushed out of the pill, portal button
   the wrong size, flyout detached from the pill) were all specificity or
   ordering accidents, not missing rules. Patching could not fix that; this
   file has ONE rule per thing.

   SOURCE OF TRUTH: washtec.com's own stylesheet,
   merged-c3158fb2b9fca7af5cecc28a82f60236-min.css, plus the rendered DOM
   captured in index.devtools. Values below are copied from it, not measured
   off a screenshot. Where we deviate, the comment says why.

   THREE RULES THIS FILE KEEPS:
   1. No bare element selectors that can out-specify a component class.
      The old sheet had `.mvii-public a { color: var(--corp-blue) }` at
      specificity (0,1,1), which beat `.pub-nav-link { color:#fff }` at
      (0,1,0). Anchors in the nav went blue; <button> nav items stayed white.
      Anchor colour here is scoped to prose containers only.
   2. Nothing is redefined later in the file. Read top to bottom, each
      selector appears once (plus its media queries, adjacent to it).
   3. Sizes that WashTec states in px stay px; sizes they state in rem stay
      rem. Converting between them is how the scaling drifted.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Typeface ──────────────────────────────────────────────────────────
   "Tisa Sans is used for all media and all kind of typography."
   — WashTec Corporate Identity & Design manual V9b, page 6.
   Subset to Latin + punctuation and converted to WOFF: 169 KB for five
   weights, down from 1.1 MB of TTFs. No italics — nothing uses them. */
@font-face { font-family: "Tisa Sans"; src: url("fonts/TisaSansPro-Light.woff") format("woff");     font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Tisa Sans"; src: url("fonts/TisaSansPro-Regular.woff") format("woff");   font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Tisa Sans"; src: url("fonts/TisaSansPro-Medium.woff") format("woff");    font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Tisa Sans"; src: url("fonts/TisaSansPro-Bold.woff") format("woff");      font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Tisa Sans"; src: url("fonts/TisaSansPro-ExtraBold.woff") format("woff"); font-weight: 800; font-style: normal; font-display: swap; }

/* ── 2. Tokens ────────────────────────────────────────────────────────────
   WashTec's :root, verbatim, renamed with our --mv prefix so it can never
   collide with portal.css. Colours we do not use are omitted; nothing here
   is invented. --container-padding is 1.25rem at EVERY breakpoint on their
   site — the old sheet stepped it to 3rem/6.25rem, which is why our pages
   never lined up with theirs at any width. Only the hero headline uses the
   100px inset, and that is its own rule. */
.mvii-public {
  --mv-white:        #ffffff;
  --mv-black:        #000000;
  --mv-blue-dark:    #123253;   /* --color-blue-dark  */
  --mv-nice-blue:    #126eb6;   /* --color-nice-blue  */
  --mv-blue-25:      #006eb7;
  --mv-blue-26:      #7f3784;
  --mv-blue-27:      #823683;
  --mv-blue-3:       #5dc2d5;
  --mv-orange:       #f39202;
  --mv-orange-hover: #123253;   /* #202: hover lands on navy - the palette has one orange */
  --mv-grey-light:   #f2f2f2;   /* --color-light-grey */
  --mv-grey-bg:      #e6e6e6;
  --mv-grey-dark:    #4c4c4c;
  --mv-placeholder:  #c3c9d6;

  --mv-gradient: linear-gradient(70deg, var(--mv-blue-dark), var(--mv-blue-25) 75%, var(--mv-blue-26) 108%, var(--mv-blue-27) 108%, var(--mv-blue-27) 108%);

  --mv-font: "Tisa Sans", ui-sans-serif, system-ui, "Segoe UI", Arial, sans-serif;
  --mv-font-size: 1.5rem;       /* --font-base-size   */
  --mv-line: 1.33;              /* --font-base-height */
  --mv-transition: .3s;

  --mv-container:        100rem;
  --mv-container-narrow:  85rem;
  --mv-container-wide:   110rem;
  --mv-container-pad:   1.25rem;
}

/* ── 3. Base ──────────────────────────────────────────────────────────────
   Scoped to .mvii-public throughout: the portal shares this document tree on
   a few pages (login), and portal.css must stay untouched. */
.mvii-public *,
.mvii-public *::before,
.mvii-public *::after { box-sizing: border-box; }

body.mvii-public {
  margin: 0;
  font-family: var(--mv-font);
  font-size: var(--mv-font-size);
  line-height: var(--mv-line);
  color: var(--mv-black);
  background: var(--mv-white);
  -webkit-font-smoothing: antialiased;
}
/* Resets and the type scale are wrapped in :where() so they carry ZERO
   specificity, exactly as washtec.com's bare `h2 { }` does. Scoping them as
   `.mvii-public h2` would make them (0,1,1) — which beats a page-level
   `.cat-rail-title { font-size: 15px }` at (0,1,0) and silently blows up
   every page-styled heading. That is the same specificity trap that turned
   the nav anchors blue, pointed the other way; :where() removes it for good. */
:where(.mvii-public) img { max-width: 100%; }
:where(.mvii-public) :is(h1, h2, h3, h4, h5, h6, p) { margin: 0; }

/* Anchor colour is scoped to PROSE ONLY. Component anchors (nav links,
   buttons, footer links, card links) colour themselves and are never
   out-specified from here. This is rule 1 at the top of the file. */
.mvii-public .pub-section p a,
.mvii-public .pub-section li a { color: var(--mv-nice-blue); }
.mvii-public a { text-decoration: none; }
.mvii-public .pub-section p a:hover,
.mvii-public .pub-section li a:hover { text-decoration: underline; }

/* ── 4. Type scale ────────────────────────────────────────────────────────
   Literal breakpoint steps, copied from washtec.com. The previous sheet
   derived every size from a single --wt-em multiplier; that is a plausible
   system but it is NOT theirs, and it is why headings scaled on a different
   curve from ours at every width between 900 and 1200px. */
:where(.mvii-public) :is(h1, .h1) { font-size: 2.125rem;  font-weight: 800; line-height: 1.41; }
:where(.mvii-public) :is(h2, .h2) { font-size: 2.125rem;  font-weight: 800; line-height: 1.41; }
:where(.mvii-public) :is(h3, .h3) { font-size: 1.5625rem; font-weight: 700; line-height: 1.28; }
:where(.mvii-public) :is(h4, .h4) { font-size: 1.375rem;  font-weight: 700; line-height: 1.28; }
:where(.mvii-public) :is(h5, .h5) { font-size: 1.25rem;   font-weight: 700; line-height: 1.28; }
:where(.mvii-public) :is(h6, .h6) { font-size: 1.125rem;  font-weight: 700; line-height: 1.28; }
@media (min-width: 1200px) {
  :where(.mvii-public) :is(h2, .h2) { font-size: 4.875rem;  line-height: 1.28; }
  :where(.mvii-public) :is(h3, .h3) { font-size: 3.3125rem; }
  :where(.mvii-public) :is(h4, .h4) { font-size: 2.875rem;  line-height: 1.16; }
  :where(.mvii-public) :is(h5, .h5) { font-size: 2.375rem;  line-height: 1.16; }
  :where(.mvii-public) :is(h6, .h6) { font-size: 2rem;      line-height: 1.16; }
}

/* ══════════════════════════════════════════════════════════════════════════
   6. HEADER
   Structure ported from their DOM, which is TWO stacked containers inside a
   fixed wrapper — not one grid with the nav on row 2:

     .m-header-wrapper           fixed, z-index 9999
       app-header.m-header       absolute, colour white, z-index 10
         header.m-header__container      GRID: logo | metamenu | select+burger
         div.m-header__desktopnavi-wrapper   plain .container holding the pill
       div.m-header--shadow      the 297px scrim, a SIBLING element

   Building the pill as row 2 of the same grid (what the old sheet did) is
   what forced the nav column wider than the container and pushed the search
   item out of the pill. As its own container it simply spans the width.
   ══════════════════════════════════════════════════════════════════════════ */
.mvii-public .pub-header {
  position: fixed; top: 0; left: 0; width: 100%; z-index: 9999;
  color: var(--mv-white);
}
/* .m-header--shadow — keeps white type readable over video or imagery. */
.mvii-public .pub-header__shadow {
  transition: all 1s;
  position: absolute; top: 0; left: 0; width: 100%; height: 297px; z-index: -1;
  pointer-events: none;
  background-image: linear-gradient(to bottom, rgb(14 38 56 / .75), #fff0);
}
@media (max-width: 1200px) { .mvii-public .pub-header__shadow { height: 78px; } }

/* .m-header-wrapper.fixed — scrolled state.
   Their fixed state sets the scrim to `position:static; height:170px`, which
   works on washtec.com because their <app-header> is absolutely positioned
   INSIDE the wrapper: the static scrim is what gives the wrapper its 170px
   height and the header content floats over it. Our bar is in normal flow, so
   copying that literally stacked 170px of scrim UNDERNEATH the bar and buried
   the top of the first content row behind a dark band. Once the header carries
   the gradient there is nothing left for the scrim to do, so it is simply
   switched off — same rendered result, without the phantom height. */
.mvii-public .pub-header.is-stuck { background: var(--mv-gradient); }
.mvii-public .pub-header.is-stuck .pub-header__shadow { display: none; }
.mvii-public .pub-header.is-stuck .pub-header__bar { transition: all 1s; padding-top: 1.25rem; padding-bottom: 1.25rem; }
.mvii-public .pub-header.is-stuck .pub-logo-img { transition: all 1s; width: 15.81rem; }
@media (min-width: 1200px) { .mvii-public .pub-header.is-stuck .pub-logo-img { width: 19.875rem; } }

/* .m-header__container — row one. Their columns are 11.75rem 1fr 2.1875rem,
   becoming 23.5rem 1fr at >=1200px. Ours uses `auto` for column one because
   our logo is a LOCKUP (mark + "CAR WASH ACADEMY"), not a single image, so a
   fixed column would clip it at some widths. Everything else is theirs. */
.mvii-public .pub-header__bar {
  transition: all 1s;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) 2.1875rem;
  gap: 1.25rem; align-items: center;
  width: 100%; max-width: var(--mv-container);
  margin: 0 auto;
  padding: 1.25rem var(--mv-container-pad) 3.25rem;
}
@media (min-width: 1200px) {
  .mvii-public .pub-header__bar {
    grid-template-columns: auto minmax(0, 1fr) auto;
    padding-top: 3.25rem; padding-bottom: 2.375rem;
  }
}
/* Below 1200px the pill is hidden and the burger takes over, so the 3.25rem
   of clearance the bar reserves for the pill has nothing to hold. Dropping it
   brings the bar to ~82px, which is what their 78px scrim is cut to cover at
   this width — with the reserve left in, the logo sat below the scrim and lost
   its contrast against the video. */
@media (max-width: 1199.98px) {
  .mvii-public .pub-header__bar { padding-bottom: 1.25rem; }
}

/* Pages without a hero: the bar carries the gradient from load, and the page
   must clear the fixed header instead of sliding under it. Decided here, not
   per page, so a new page cannot forget it.

   The clearance is PADDING ON THE BODY, not a margin on `main` or on the first
   section. The old sheet targeted those two selectors, so any page that used
   neither — catalog.html is a bespoke one-pager with a <section class="cat-
   section"> at the top — got no clearance at all and rendered its first 160px
   underneath the header. Padding on the body cannot be missed by a page shape,
   and because everything here is border-box it also survives catalog's
   `html, body { height: 100% }` one-pager trick. */
.mvii-public.pub-nohero .pub-header { background: var(--mv-gradient); }
.mvii-public.pub-nohero .pub-header__shadow { display: none; }
/* A page that opens with a .pub-intro band needs NO body padding: the band's
   own 6.6875rem / 16.1875rem of top padding is what clears the header on
   washtec.com, and it does it behind the gradient instead of behind a white
   gap. The padding below is the fallback for pages that do not have an intro
   band yet. */
body.mvii-public.pub-nohero:not(.pub-hasintro) { padding-top: 6rem; }
@media (min-width: 1200px) { body.mvii-public.pub-nohero:not(.pub-hasintro) { padding-top: 14rem; } }

/* ── 6a. Logo lockup ──────────────────────────────────────────────────────
   .m-header__logo--markvii is 11.75rem, 19.875rem at >=1200px. Their image IS
   the whole lockup; ours has to add the academy line beside it.

   Daniel 8/21: "the car wash academy needs to be a little more prevalent, the
   same size as mark vii logo text — not the logo box, just the text — and
   centred up and down so it looks like it's a part of it."
   Then, on seeing it shipped: "it should be on a single line, it's 2 lines now
   — car wash line 1, academy line 2. One line centred vertical I think might
   get the job done. It might need a little bit more size."

   So: ONE line, vertically centred on the mark, at 1.75rem/800 with .06em
   tracking above 1200px. Measured from assets/logo-markvii.png (284x63), the
   MARK VII wordmark occupies y=14..48 — 35px of the 63px image — and renders
   about 39px tall at the >=1200px logo size. A 28px extrabold line has a cap
   height near 20px, so it reads as a companion to the wordmark rather than a
   caption, and sixteen characters land around 300px, which the header has
   room for beside a 318px mark.

   The lockup is wrapped in `white-space: nowrap` deliberately: if it ever runs
   out of room the correct failure is the metamenu compressing (which it does,
   below), not this line breaking back into two. */
.mvii-public .pub-logo {
  grid-column: 1 / 1; justify-self: start; align-self: center;
  display: flex; align-items: center; gap: 0.9375rem;
  text-decoration: none; color: var(--mv-white);
}
.mvii-public .pub-logo-img {
  width: 11.75rem; max-width: 100%; height: auto;
  display: block; align-self: center; transition: all 1s;
}
@media (min-width: 1200px) { .mvii-public .pub-logo-img { width: 19.875rem; } }

.mvii-public .pub-logo-rule {
  display: none; width: 1px; height: 2.25rem; align-self: center;
  background: rgb(255 255 255 / .45);
}
.mvii-public .pub-logo-sub {
  display: none; align-self: center;
  font-family: var(--mv-font); font-weight: 800;
  font-size: 1.25rem; line-height: 1; letter-spacing: .06em;
  text-transform: uppercase; color: var(--mv-white); white-space: nowrap;
}
@media (min-width: 900px) {
  .mvii-public .pub-logo-rule { display: block; }
  .mvii-public .pub-logo-sub  { display: block; }
}
@media (min-width: 1200px) {
  .mvii-public .pub-logo-sub  { font-size: 1.75rem; }
  .mvii-public .pub-logo-rule { height: 2.875rem; }
}

/* ── 6b. Metamenu (utility row) ───────────────────────────────────────────
   .m-header__metamenu: hidden below 1200px, 30px gap, right-aligned.
   Links are 18px/20px white. */
.mvii-public .pub-utility {
  grid-column: 2 / 2; grid-row: 1;
  display: none; gap: 30px; align-items: center; justify-content: flex-end;
  flex-wrap: nowrap; min-width: 0;
}
/* Between 1200 and 1366px the single-line academy lockup and the metamenu are
   competing for the same row. The metamenu gives ground first — it closes to a
   16px gap — because the lockup breaking back onto two lines is the failure
   Daniel asked us to remove. */
@media (min-width: 1200px) and (max-width: 1365.98px) {
  .mvii-public .pub-utility { gap: 16px; }
  .mvii-public .pub-utility-link { font-size: 16px; }
}
@media (min-width: 1200px) { .mvii-public .pub-utility { display: flex; } }
.mvii-public .pub-utility-link {
  display: block; font-size: 18px; line-height: 20px;
  color: var(--mv-white); text-decoration: none;
}
.mvii-public .pub-utility-link:hover { text-decoration: underline; }

/* ── 6c. Portal button ────────────────────────────────────────────────────
   Daniel 8/21: "it should match the global button on washtec as in size,
   length etc, as the wording changes depending if you're logged in."

   Their GLOBAL control is a <select class="m-select m-header__country-select">:
     .m-select                 border:0; border-radius:1.15625rem;
                               background-color:#006eb7; colour:#fff;
                               line-height:1; arrow at right 1.25rem centre
     .m-header__country-select justify-self:end; align-self:center;
                               font-size:1rem; width:191px; height:28px;
                               padding:2.4px 21px; grid-column 2, 3 at >=1200px
                               display:none below 768px

   The FIXED 191x28 is precisely why a variable label is safe there, so ours
   is fixed too and the label ellipsises rather than resizing the control. */
.mvii-public .pub-portal {
  grid-column: 2 / 2; justify-self: end; align-self: center;
  position: relative; display: block; margin: 0; opacity: 1;
  width: 191px; height: 28px; padding: 2.4px 21px;
  border: 0; border-radius: 1.15625rem;
  background-color: var(--mv-blue-25);
  font-family: var(--mv-font); font-size: 1rem; line-height: 1;
  color: var(--mv-white); text-decoration: none;
  transition: background-color var(--mv-transition);
}
@media (max-width: 768px)  { .mvii-public .pub-portal { display: none; } }
@media (min-width: 1200px) { .mvii-public .pub-portal { grid-column: 3 / 3; } }
.mvii-public .pub-portal:hover { background-color: var(--mv-nice-blue); }
.mvii-public .pub-portal-label {
  display: block; height: 100%;
  line-height: 23.2px;   /* 28px box less the 2.4px padding top and bottom */
  padding-right: 1.25rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 400; letter-spacing: .02em;
}
.mvii-public .pub-portal-chev {
  position: absolute; top: 50%; right: 1.25rem; transform: translateY(-50%);
  width: 1.5rem; height: .875rem;   /* .m-select background-size */
  pointer-events: none;
}

/* ── 6d. Burger + mobile menu ─────────────────────────────────────────────
   .m-header__menu-button: 2.1875rem square, column 3, hidden at >=1200px.
   Theirs is an SVG background; ours draws the bars in CSS so there is no
   extra request and no icon file to lose in a deploy. */
.mvii-public .pub-burger {
  grid-column: 3 / 3; grid-row: 1; justify-self: end; align-self: center;
  width: 2.1875rem; height: 2.1875rem; padding: 0; border: 0;
  background: none; cursor: pointer; display: block;
}
@media (min-width: 1200px) { .mvii-public .pub-burger { display: none; } }
.mvii-public .pub-burger span,
.mvii-public .pub-burger span::before,
.mvii-public .pub-burger span::after {
  display: block; width: 2.1875rem; height: 3px;
  background: var(--mv-white); border-radius: 2px; content: '';
}
.mvii-public .pub-burger span { position: relative; }
.mvii-public .pub-burger span::before { position: absolute; top: -10px; }
.mvii-public .pub-burger span::after  { position: absolute; top: 10px; }

.mvii-public .pub-mobile-menu {
  display: none; position: absolute; top: 100%; left: 0; width: 100%;
  max-height: calc(100vh - 5rem); overflow-y: auto;
  background-color: var(--mv-blue-dark);
  padding: 20px var(--mv-container-pad);
}
.mvii-public .pub-mobile-menu.open { display: block; }
@media (min-width: 1200px) { .mvii-public .pub-mobile-menu { display: none !important; } }
.mvii-public .pub-mobile-menu a {
  display: block; color: var(--mv-white); text-decoration: none;
  font-size: 1.125rem; line-height: 2.22;
  background-color: rgb(255 255 255 / .05);
  padding: 5px 0 5px 20px; margin-bottom: .3125rem;
}
.mvii-public .pub-mobile-menu a.sub { padding-left: 40px; opacity: .85; font-size: 1rem; }

/* ── 6e. The nav pill ─────────────────────────────────────────────────────
   .m-header__desktopnavi lives in its OWN .container below the bar.
     display:flex; justify-content:flex-start (space-between with modifier)
     border-radius:33px; box-shadow:0 3px 11px 0 rgb(0 0 0 / .1)
     background-color:rgb(255 255 255 / .1); position:relative
     display:none below 1200px
   There is NO gap property on it. The old sheet added
   `gap: calc(var(--wt-em) * 4.63)` in a trailing block — 111px per gap at
   desktop, roughly 780px of phantom width — which is what shoved the search
   item out of the pill. Items are spaced by space-between and their own
   9px/10px padding, nothing else. */
.mvii-public .pub-navwrap {
  width: 100%; max-width: var(--mv-container);
  margin: 0 auto; padding: 0 var(--mv-container-pad);
}
.mvii-public .pub-nav {
  display: none; flex-direction: row; justify-content: space-between;
  border-radius: 33px;
  box-shadow: 0 3px 11px 0 rgb(0 0 0 / .1);
  background-color: rgb(255 255 255 / .1);
  position: relative;
}
@media (min-width: 1200px) { .mvii-public .pub-nav { display: flex; } }

.mvii-public .pub-nav-item { padding: 9px 10px; pointer-events: auto; }
.mvii-public .pub-nav-item:hover        { background-color: rgb(18 50 83 / .95); border-radius: 26.5px 26.5px 0 0; }
.mvii-public .pub-nav-item.nosub:hover  { border-radius: 26.5px; }
.mvii-public .pub-nav-item.active-item  { background-color: rgb(18 50 83 / .95); border-radius: 26.5px; }
.mvii-public .pub-nav-item.open         { background-color: rgb(18 50 83 / .95); border-radius: 26.5px 26.5px 0 0; }

/* One rule for the label, and it applies to <a> AND <button> identically.
   In the old sheet these two rendered different colours because the anchor
   picked up a prose colour that out-specified the component. */
.mvii-public .pub-nav-link {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--mv-font);
  font-size: 24px; font-weight: 400; letter-spacing: normal;
  /* 33px, not `normal`. Their line-height:normal computes to 33px here, which
     is why their flyout is pinned at top:51px (9 + 33 + 9). Leaving it as
     `normal` means the line box grows with the font weight, so the current
     page's nav item made the whole header 2px taller than on every other page
     — exactly the shift Daniel asked us to eliminate ("the text and header etc
     do not shift between pages"). A stated line-height cannot drift. */
  line-height: 33px;
  text-decoration: none; color: var(--mv-white); white-space: nowrap;
  border: 0; background: none; padding: 0; margin: 0; cursor: pointer;
}
.mvii-public .pub-nav-link:hover { text-decoration: none !important; color: var(--mv-white); }
/* The current page is marked by the filled pill on the ITEM, the way washtec.com
   marks it. It deliberately does not also bold the label: a weight change alters
   the label's width and would nudge every other item along the pill. */
.mvii-public .pub-nav-link.active { font-weight: 400; }
.mvii-public .pub-nav-caret { width: 12px; height: 12px; flex-shrink: 0; opacity: .85; }

/* ── 6f. Flyout ───────────────────────────────────────────────────────────
   .m-header__desktopnavi--sub, ported exactly:
     position:absolute; width:100%; left:0; top:51px
     background rgb(18 50 83 / .9); min-height:408px; max-height:426px
     padding:31px 30px 40px; border-radius:0 0 26.5px 26.5px; z-index:-1
     display:flex; flex-direction:COLUMN; flex-wrap:wrap; gap:30px
     align-content:flex-start
   Two things the old sheet had wrong, and both are the bugs Daniel saw:

   (a) It used flex-direction:row with hand-made columns. Theirs is a single
       wrapping COLUMN — items flow down, then wrap into a new column once
       they hit the 408px floor. That is why their menu forms even columns.
   (b) top was hard-coded to 51px. 51px is THEIR item height (9 + 33 + 9).
       Our item is a different height, so the panel hung 5px below the pill
       and the hover broke the moment the cursor crossed the gap — exactly
       "when leaving hovering to go to click on one of the options it
       disappears". Positioning against the nav at top:100% makes the panel
       start where the pill ends at any size, so there is no gap to cross
       and no bridge hack is needed.

   The panel is a DOM child of the nav item, so hovering it keeps the item
   hovered; that is what holds the menu open while the pointer travels. */
/* Two details make the panel read as ONE shape with the pill rather than a
   box parked underneath it — Daniel 8/21, twice: "the box is not connected to
   the pill when hovering".

   (1) The pill's bottom corners are rounded at 33px, so the fill curves away
       from the panel's square top corners and leaves a notch at each end.
       washtec.com fills those notches with two 27px corner pieces
       (.m-header__desktopnavi--nedge--left / --right, opacity 0 -> 90% on
       hover). Squaring the pill's bottom corners while a flyout is open gets
       to the same silhouette without shipping two corner images: pill top
       rounded 33px, straight sides all the way down, panel bottom rounded
       26.5px. `:has()` does it declaratively; the .flyout-open class the shell
       script also sets is the belt-and-braces path.
   (2) The header sits on a fractional pixel offset (its height is not a whole
       number at every zoom level), so an exact top:100% can leave a hairline
       of pill background showing through between the two. The -1px pull
       guarantees they overlap instead of abutting. */
.mvii-public .pub-nav:has(> .pub-nav-item:hover > .pub-dd),
.mvii-public .pub-nav:has(> .pub-nav-item.open),
.mvii-public .pub-nav.flyout-open {
  border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}
.mvii-public .pub-dd {
  position: absolute; top: 100%; margin-top: -1px; left: 0; width: 100%;
  background-color: rgb(18 50 83 / .9);
  min-height: 408px; max-height: 426px;
  padding: 31px 30px 40px;
  border-radius: 0 0 26.5px 26.5px;
  z-index: -1;
  display: none; flex-direction: column; flex-wrap: wrap;
  align-content: flex-start; gap: 30px;
}
.mvii-public .pub-nav-item:hover > .pub-dd,
.mvii-public .pub-nav-item.open  > .pub-dd { display: flex; }
.mvii-public .pub-dd-item { min-width: 300px; display: flex; flex-direction: column; gap: 10px; flex-wrap: nowrap; }
.mvii-public .pub-dd-link {
  display: block; font-family: var(--mv-font);
  font-size: 24px; font-weight: 400; line-height: normal;
  color: var(--mv-white); text-decoration: none; white-space: nowrap;
}
.mvii-public .pub-dd-link:hover { text-decoration: underline; color: var(--mv-white); }

/* ── 6g. Search ───────────────────────────────────────────────────────────
   On washtec.com search is simply the last pill item — a --nosub item whose
   link reads "Search". Ours is the same item with the magnifier ahead of the
   word, and the field expands in place. Because the pill is space-between
   with no gap, the field grows into the pill's free space instead of pushing
   anything out of it. */
.mvii-public .pub-nav-search { display: flex; align-items: center; }
.mvii-public .pub-search-icon { width: 26px; height: 26px; flex-shrink: 0; display: block; }
.mvii-public .pub-search-form {
  width: 0; overflow: hidden; transition: width var(--mv-transition);
  display: flex; align-items: center;
}
.mvii-public .pub-nav-search.open .pub-search-form { width: 14rem; margin-left: 10px; }
.mvii-public .pub-nav-search.open .pub-search-label { display: none; }
.mvii-public .pub-search-form input {
  width: 100%; border: 0; border-bottom: 1px solid rgb(255 255 255 / .6);
  background: none; color: var(--mv-white); font-family: var(--mv-font);
  font-size: 1.125rem; padding: 2px 6px; outline: none;
}
.mvii-public .pub-search-form input::placeholder { color: rgb(255 255 255 / .65); }

/* ══════════════════════════════════════════════════════════════════════════
   7. HERO — .m-header-video
   width:100%; height:100vh. The headline is ABSOLUTE at top:290px inside a
   1600px band with 100px side padding — it is NOT vertically centred, which
   is the single biggest reason our hero always "read" differently.
   ══════════════════════════════════════════════════════════════════════════ */
.mvii-public .pub-hero {
  display: block; width: 100%; height: 100vh;
  position: relative; overflow: hidden;
  background: var(--mv-gradient); color: var(--mv-white);
}
.mvii-public .pub-hero-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0; pointer-events: none;
}
.mvii-public .pub-hero-video--mobile { display: none; }
@media (max-width: 900px) {
  .mvii-public .pub-hero-video--desktop { display: none; }
  .mvii-public .pub-hero-video--mobile  { display: block; }
}
/* A scrim over the footage so white type holds on bright frames — the job
   .m-header--shadow does for the header. */
.mvii-public .pub-hero::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom, rgb(14 38 56 / .55) 0%, rgb(14 38 56 / .25) 45%, rgb(14 38 56 / .5) 100%);
}
.mvii-public .pub-hero-inner {
  position: absolute; top: 290px; z-index: 2;
  max-width: 1600px; width: 100%; left: 50%; transform: translate(-50%, 0);
  padding: 0 100px;
}
@media (max-width: 1200px) { .mvii-public .pub-hero-inner { top: 130px; } }
@media (max-width: 900px)  { .mvii-public .pub-hero-inner { padding: 0 20px; } }
.mvii-public .pub-hero h1 {
  color: var(--mv-white); margin-bottom: 0;
  font-size: 4.875rem; font-weight: 800; line-height: 1.28;
}
@media (max-width: 900px) { .mvii-public .pub-hero h1 { font-size: 3.375rem; line-height: 1.24; } }
@media (max-width: 768px) { .mvii-public .pub-hero h1 { font-size: 2.625rem; line-height: 1.2; } }
/* On the hero the gradient-fill accent is overridden back to solid white —
   a gradient that ends in violet is unreadable over the footage. */
.mvii-public .pub-hero h1 .grad {
  background: none; -webkit-text-fill-color: var(--mv-white); color: var(--mv-white);
}
.mvii-public .pub-hero p {
  margin: 1.5rem 0 30px; max-width: 42ch;
  font-size: var(--mv-font-size); line-height: var(--mv-line);
  color: rgb(255 255 255 / .92);
}

/* ══════════════════════════════════════════════════════════════════════════
   8. BUTTONS — .m-button, verbatim.
   Theirs is a THIN bordered button, min-width 11.375rem, 1.125rem/700,
   .0625rem border, 1.25rem radius, that INVERTS on hover. No transform, no
   shadow: colour, border-colour and background over .3s and nothing else.
   ══════════════════════════════════════════════════════════════════════════ */
.mvii-public .pub-btn {
  display: inline-flex; justify-content: center; align-items: center; gap: 8px;
  min-width: min(11.375rem, 100%); width: fit-content;
  padding: .3125rem 1.25rem;
  border: .0625rem solid var(--mv-blue-dark);
  border-radius: 1.25rem;
  background-color: var(--mv-white); color: var(--mv-blue-dark);
  font-family: var(--mv-font); font-size: 1.125rem; font-weight: 700; line-height: 1.625rem;
  text-decoration: none; cursor: pointer;
  transition-property: color, border-color, background-color;
  transition-duration: var(--mv-transition);
}
.mvii-public .pub-btn:hover         { border-color: var(--mv-white);     background-color: var(--mv-blue-dark); color: var(--mv-white); }
.mvii-public .pub-btn-primary       { border-color: var(--mv-nice-blue); background-color: var(--mv-nice-blue); color: var(--mv-white); }
.mvii-public .pub-btn-primary:hover { border-color: var(--mv-nice-blue); background-color: var(--mv-white);     color: var(--mv-nice-blue); }
.mvii-public .pub-btn-dark          { border-color: var(--mv-blue-dark); background-color: var(--mv-blue-dark); color: var(--mv-white); }
.mvii-public .pub-btn-dark:hover    { border-color: var(--mv-blue-dark); background-color: var(--mv-white);     color: var(--mv-blue-dark); }
.mvii-public .pub-btn-light         { border-color: var(--mv-white);     background-color: var(--mv-white);     color: var(--mv-blue-dark); }
.mvii-public .pub-btn-light:hover   { border-color: var(--mv-white);     background-color: transparent;         color: var(--mv-white); }
.mvii-public .pub-btn-outline       { border-color: rgb(255 255 255 / .75); background-color: transparent;      color: var(--mv-white); }
.mvii-public .pub-btn-outline:hover { border-color: var(--mv-white);     background-color: var(--mv-white);     color: var(--mv-blue-dark); }
/* Orange is the one CTA accent, capped at a single use per page. */
.mvii-public .pub-btn-orange        { border-color: var(--mv-orange);    background-color: var(--mv-orange);    color: var(--mv-white); }
.mvii-public .pub-btn-orange:hover  { border-color: var(--mv-orange-hover); background-color: var(--mv-orange-hover); color: var(--mv-white); }

/* ══════════════════════════════════════════════════════════════════════════
   9. SECTIONS — .m-section, 2.5rem / 3.125rem vertical rhythm.
   ══════════════════════════════════════════════════════════════════════════ */
.mvii-public .pub-section { padding-top: 2.5rem; padding-bottom: 2.5rem; }
@media (min-width: 900px) { .mvii-public .pub-section { padding-top: 3.125rem; padding-bottom: 3.125rem; } }
.mvii-public .pub-section.alt        { background-color: var(--mv-grey-light); }
.mvii-public .pub-section.blue-dark  { background-color: var(--mv-blue-dark); color: var(--mv-white); }
.mvii-public .pub-section.gradient-1 { background-image: var(--mv-gradient); color: var(--mv-white); }

.mvii-public .pub-section-inner {
  display: flex; flex-direction: column; gap: 2.5rem;
  width: 100%; max-width: var(--mv-container);
  padding-left: var(--mv-container-pad); padding-right: var(--mv-container-pad);
  margin: 0 auto;
}
.mvii-public .pub-section-inner.narrow { max-width: var(--mv-container-narrow); }
.mvii-public .pub-section-inner.wide   { max-width: var(--mv-container-wide); }

/* Their headline treatment: in default and gradient-2/3 sections every
   heading is filled with the corporate gradient and sized to its own text.
   In gradient-1 and blue-dark sections the fill is dropped for solid white.
   This is a page-wide behaviour on washtec.com, not an opt-in class, which
   is why our headings never looked like theirs — ours only got the gradient
   where a .grad span had been added by hand. */
/* Deliberately kept at ONE class of specificity (the `:not([class])` is all
   that counts here — the scope is wrapped in :where()). Anything that needs to
   switch the fill off — a gradient band, a dark panel, a page-level heading
   style — then wins on ordinary specificity instead of needing !important.
   Written as `.mvii-public .pub-section :where(...)` it was (0,3,1), which
   beat every override and made headings on dark panels invisible. */
:where(.mvii-public .pub-section) :where(h1, h2, h3, h4, h5, h6):not([class]),
:where(.mvii-public) .grad {
  width: fit-content;
  background-image: var(--mv-gradient);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: #fff0;
}
.mvii-public .pub-section.gradient-1 :where(h1, h2, h3, h4, h5, h6):not([class]),
.mvii-public .pub-section.blue-dark  :where(h1, h2, h3, h4, h5, h6):not([class]),
.mvii-public .pub-section.gradient-1 .grad,
.mvii-public .pub-section.blue-dark  .grad {
  background-image: none; -webkit-text-fill-color: var(--mv-white); color: var(--mv-white);
}
.mvii-public .pub-section.gradient-1 p,
.mvii-public .pub-section.blue-dark  p { color: var(--mv-white); }

/* "Coming soon" marker — violet, per the palette rule (no green, no amber).
   It lived inside the cards section that the session-19 cleanup removed and
   went out with it while index.html was still using it. Restored here, where
   it belongs to no block that might later be cut as a unit. */
.mvii-public .pub-soon {
  display: inline-block; vertical-align: middle; margin-left: 8px;
  padding: 3px 10px; border-radius: 999px;
  background-color: rgb(127 55 132 / .12); color: #7f3784;
  font-family: var(--mv-font); font-size: 11px; font-weight: 700;
  line-height: 1.6; letter-spacing: .08em; text-transform: uppercase;
  background-image: none; -webkit-text-fill-color: #7f3784;
}

/* ══════════════════════════════════════════════════════════════════════════
   12. FOOTER — .m-footer, rebuilt from their rendered DOM rather than from
   the class list alone.

   Daniel 8/21: "the footer doesn't even seem similar, I do not see the social
   media stuff on ours at the bottom like theirs."

   He is right, and the reason is that the earlier version was built from the
   .m-footer__container rule in their stylesheet — which their homepage does
   not actually use. Their live markup is:

     <footer class="m-footer">
       <div class="container m-footer__icons">        <- a THREE-part row
         <div class="m-footer__meta">                    copyright + legal links
         <div class="m-footer__icons--left">             award / partner badges
         <div class="m-footer__icons--right">            social icons
       </div>
     </footer>

   space-between, centred, going to a left-aligned column below 1300px. Ours
   was a centred stack of links with the copyright underneath — the same
   ingredients in a completely different arrangement, which is exactly why it
   read as a different family.

   The badges column is theirs (VDA, kununu, German Brand Award). We have no
   equivalent accreditations to show, so that column is simply absent from our
   markup — the row still resolves because it is space-between, and the social
   icons stay hard right where theirs are.
   ══════════════════════════════════════════════════════════════════════════ */
.mvii-public .pub-footer {
  background-color: var(--mv-white);
  font-size: .875rem; line-height: 2.86; color: var(--mv-blue-dark);
}
.mvii-public .pub-footer-inner {
  margin-top: 1.875rem; padding-bottom: 1.875rem;
  display: flex; flex-direction: row; justify-content: space-between; align-items: center;
  width: 100%; max-width: var(--mv-container); margin-left: auto; margin-right: auto;
  padding-left: 20px; padding-right: 20px;
}
@media (max-width: 1300px) {
  .mvii-public .pub-footer-inner { flex-direction: column; align-items: start; }
}
.mvii-public .pub-footer__meta { display: flex; flex-direction: column; align-items: start; }
.mvii-public .pub-footer__copyright { font-size: inherit; }
.mvii-public .pub-footer-links {
  display: flex; font-weight: 700; align-items: start; gap: 10px; flex-direction: column;
}
@media (min-width: 600px) { .mvii-public .pub-footer-links { flex-direction: row; } }
.mvii-public .pub-footer-link { display: block; line-height: 20px; color: inherit; text-decoration: none; }
.mvii-public .pub-footer-link:hover { text-decoration: underline; }

/* .m-footer__icons--center: the badge column, 48px tall like theirs. The
   group mark is a wordmark until marketing's SVG replaces it (public-shell.js). */
.mvii-public .pub-footer__badges { display: flex; align-items: center; justify-content: center; }
@media (max-width: 1300px) { .mvii-public .pub-footer__badges { margin-top: 1.25rem; } }
.mvii-public .pub-footer__badge { display: flex; align-items: baseline; gap: .375rem; height: 48px; text-decoration: none; color: var(--mv-blue-dark); line-height: 48px; }
.mvii-public .pub-footer__badge img { height: 48px; width: auto; display: block; }
.mvii-public .pub-footer__badge-brand { font-weight: 800; font-size: 1.5rem; letter-spacing: -.01em; }
.mvii-public .pub-footer__badge-sub { font-weight: 400; font-size: 1rem; text-transform: uppercase; letter-spacing: .16em; }
.mvii-public .pub-footer__badge:hover { text-decoration: none; opacity: .8; }

/* .m-footer__icons--right: a row, 40px between icons (21px below 1300px),
   icons 48px tall. Theirs are black PNGs; ours are inline SVG brand glyphs in
   currentColor, so there is no image request and nothing to lose in a deploy. */
.mvii-public .pub-footer__social { display: flex; flex-direction: row; align-items: center; }
@media (max-width: 1300px) { .mvii-public .pub-footer__social { margin-top: 1.25rem; } }
.mvii-public .pub-footer__social-icon { margin-left: 40px; display: block; color: var(--mv-black); }
.mvii-public .pub-footer__social-icon:first-child { margin-left: 0; }
@media (max-width: 1300px) { .mvii-public .pub-footer__social-icon { margin-left: 21px; } }
.mvii-public .pub-footer__social-icon svg { height: 48px; width: auto; display: block; fill: currentColor; }
.mvii-public .pub-footer__social-icon:hover { color: var(--mv-nice-blue); }

/* ══════════════════════════════════════════════════════════════════════════
   13. Right-edge contact tab — .m-home-section__contact, ported exactly

   SESSION-20 AUDIT. This block was built from a description, not from their
   CSS, and it showed. Their rule, verbatim from merged-c3158fb2…min.css:

     .m-home-section__contact{position:fixed;right:0;bottom:11.875rem;
       width:4.125rem;height:4.125rem;background:var(--color-orange)
       url(icon-floatingcontact.svg) center center no-repeat;
       background-size:2.375rem 2.375rem;border-radius:10px 0 0 10px;
       box-shadow:0 4px 20px 0 rgb(0 0 0/.5);z-index:4;
       line-height:0;font-size:0;color:#fff0}
     @media (min-width:900px){ …{width:8.25rem;height:8.25rem;
       background-size:4.75rem 4.75rem} }

   Three things were wrong with ours and all three are fixed here:

   1. THE LABEL. Their markup is `<a class="m-home-section__contact">Contact</a>`
      and the CSS then sets font-size:0, line-height:0 and color:#fff0 — the
      word is in the DOM for screen readers and assistive tech and is NEVER
      PAINTED. Ours printed "Get in touch" in 12px white on the tab. That is
      the single most visible instance of "we have something they do not
      have": theirs is a wordless icon button. The text stays in the markup,
      the way they do it, so the link still announces itself.
   2. THE SHAPE. Theirs is a SQUARE — 66px, growing to 132px at ≥900px — with
      the icon at 38px / 76px. Ours was a 92px-wide padded column sized to fit
      the words, which is why it read as a different object entirely.
   3. THE SHADOW. Theirs carries 0 4px 20px rgb(0 0 0/.5); ours had none, so
      it sat flat against the page instead of floating over it.

   Kept from Daniel (8/20): the destination is contact.html and the icon is a
   message glyph, "we do not have a call so that will have to be a link to a
   contact form page instead with an icon to represent that."

   Their hide rule is `.scroll-lock ...{display:none}` — they hide it while a
   modal has the body locked, not at a breakpoint. Ours hid it under 700px;
   that is now matched to theirs, so the tab is present at every width the way
   it is on washtec.com.
   ══════════════════════════════════════════════════════════════════════════ */
.mvii-public .pub-contact-tab {
  position: fixed; right: 0; bottom: 11.875rem; z-index: 4;
  display: flex; align-items: center; justify-content: center;
  width: 4.125rem; height: 4.125rem; padding: 0;
  background-color: var(--mv-orange);
  border-radius: 10px 0 0 10px;
  box-shadow: 0 4px 20px 0 rgb(0 0 0 / .5);
  text-decoration: none;
  /* the label is present for assistive tech and never painted — their trick */
  line-height: 0; font-size: 0; color: #fff0;
  transition: background-color var(--mv-transition);
}
.mvii-public .pub-contact-tab:hover,
.mvii-public .pub-contact-tab:focus-visible { background-color: var(--mv-orange-hover); color: #fff0; }
.mvii-public .pub-contact-tab svg {
  width: 2.375rem; height: 2.375rem; flex-shrink: 0;
  stroke: var(--mv-white); fill: none; stroke-width: 1.8;
}
@media (min-width: 900px) {
  .mvii-public .pub-contact-tab { width: 8.25rem; height: 8.25rem; }
  .mvii-public .pub-contact-tab svg { width: 4.75rem; height: 4.75rem; }
}
.scroll-lock .mvii-public .pub-contact-tab,
.mvii-public.scroll-lock .pub-contact-tab { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   14. Cleanup — public pages must never show portal furniture.
   ══════════════════════════════════════════════════════════════════════════ */
.mvii-public .navbar,
.mvii-public .topbar,
.mvii-public .auth-nav { display: none !important; }


/* ══════════════════════════════════════════════════════════════════════════
   REMOVED IN THE SESSION-19 REBUILD — recorded so nobody re-adds them by
   reflex when an old page turns up:

     .pub-container / --narrow / --wide   superseded by .pub-section-inner,
                                          which is the same flex container
                                          with the section's padding on it
     .pub-grid / -2 / -3, .pub-card,
     .pub-card-icon, .pub-card-link       superseded by .pub-teasers /
                                          .pub-teaser and .pub-cards /
                                          .pub-cardp in public-blocks.css
     .pub-form-card, .pub-field,
     .pub-label, .pub-input, .pub-select,
     .pub-textarea, .pub-form-row         superseded by .pub-form and its
                                          .pub-form__* parts
     .pub-h2, .pub-lede                   superseded by real headings inside
                                          .pub-content-block, which is how
                                          washtec.com sets body copy
     .pub-hero-actions                    superseded by .pub-actions and
                                          .pub-intro__actions
     .pub-prose                           never adopted by any page

   Every one of them had zero remaining references at the time of removal,
   checked by parsing the class attributes out of all sixteen public pages
   plus public-shell.js rather than by eye.
   ══════════════════════════════════════════════════════════════════════════ */
