@charset "UTF-8";
/* =====================================================================
   MarkNtel Advisors - Shared components

   1. Site header
   ===================================================================== */

/* The scroll sentinel assets/js/modules/site-header.js observes to set
   [data-scrolled] - see that file, and the header's own [data-scrolled]
   rule below. Zero-size and unfocusable; its only job is to have a
   position IntersectionObserver can watch. */
.site-header__sentinel{
  display:block;
  inline-size:0;block-size:0;
}

/* ---------------------------------------------------------------------
   The bar itself

   Sticky, not fixed: a fixed header needs every page to reserve its own
   height as top padding by hand, and the three pages that will carry
   this do not currently agree on one (the vendor header they replace
   left that to Bootstrap's own document flow). Sticky costs nothing
   extra and cannot drift out of sync with a height that changes at a
   breakpoint.

   THE CONTAINER IS THE HEADER'S OWN, not Bootstrap's .container. The
   old chrome needed a !important patch in every page's stylesheet to
   widen Bootstrap's 1240px cap to this site's 1560px measure (see
   home-page/page.css's now-deleted section 2, and header/NOTES.md,
   "Why the old container patch is gone"). --mn-measure and --mn-gutter
   are the same tokens every band on every delivered page is built from,
   so this container's edges land on the same line as the content below
   it without a second cap to keep in sync.
   --------------------------------------------------------------------- */
.site-header{
  position:sticky;
  inset-block-start:0;
  z-index:40;
  background:var(--paper);
  border-block-end:2px solid var(--rule);
  font-family:var(--ui);
}
/* The only motion the header carries on its own: the hairline and the
   surface beneath it deepen very slightly once the page has scrolled
   past the header's own height, giving it just enough separation from
   the content sliding under it. Driven by [data-scrolled] rather than a
   :hover-style pseudo-class because there is nothing to hover - see
   assets/js/modules/site-header.js, which sets it from an
   IntersectionObserver on a sentinel at the top of <main>. No JS, no
   change: the header still reads perfectly with the plain 2px rule. */
.site-header[data-scrolled]{
  border-block-end-color:var(--ocean);
  box-shadow:0 1px 0 rgb(4 11 52 / 4%),0 12px 24px -18px rgb(4 11 52 / 22%);
}
@media (prefers-reduced-motion: no-preference){
  .site-header{transition:box-shadow .25s var(--ease),border-color .25s var(--ease);}
}

.site-header__bar{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  column-gap:clamp(16px,2.4vw,40px);
  max-inline-size:var(--mn-measure);
  margin-inline:auto;
  padding-inline:var(--mn-gutter);
  padding-block:14px;
}
/* ---------------------------------------------------------------------
   Brand

   ONE <img>, not the vendor chrome's two (a mobile copy and a desktop
   copy, swapped with Bootstrap's d-md-none/d-none d-md-flex). Both
   images were the same file at two hand-set sizes - a duplicate DOM
   node whose only job was breakpoint-switching an identical logo, which
   is exactly the wrapper/duplication CLAUDE.md's DOM rule rules out.
   One element, sized with clamp(), does the same job.

   order:1 here, and order:2/3/4 on the toggle/search/nav below, are the
   ONLY thing setting this element's flex order at mobile - brand first,
   toggle pinned to the row's end, then (only while the toggle is open)
   the search field and nav list each on a full-width line beneath.
   Order is independent of DOM order on purpose; see the top comment on
   header/header-partial.html for why the DOM order itself has a
   different job (the sibling combinator below). --------------------- */
.site-header__brand{
  order:1;
  flex:none;
  display:flex;
  align-items:center;
  border-radius:2px;
}
.site-header__brand img{
  display:block;
  inline-size:clamp(140px,15vw,208px);
  block-size:auto;
}

/* ---------------------------------------------------------------------
   The mobile toggle - a <details>, not a JS-bound button

   CLAUDE.md: "Disclosure UI... uses <details>/<summary> where the
   behaviour is plain show/hide." Opening the primary nav on a phone is
   exactly that - one panel, no coordination with anything else - so the
   whole interaction is native. With JavaScript disabled this still
   opens and closes correctly; assets/js/modules/site-header.js adds
   nothing to this part at all.

   It is a SIBLING of .site-nav, not its parent - .site-nav needs to be
   a plain always-visible flex row at desktop width, and a <details> that
   wraps content forces that content hidden by the UA stylesheet whenever
   it is closed, which a desktop-width override would have to fight on
   every browser. Wiring visibility through the general sibling
   combinator below sidesteps that entirely: the two elements' desktop
   behaviour is now just "ignore the toggle" rather than "override it". */
.site-header__toggle{
  order:2;
  flex:none;
  margin-inline-start:auto;
}
.site-header__toggle > summary{
  display:flex;
  align-items:center;
  justify-content:center;
  inline-size:44px;block-size:44px;
  border-radius:var(--r);
  color:var(--ink);
  cursor:pointer;
  list-style:none;
}
.site-header__toggle > summary::-webkit-details-marker{display:none;}
.site-header__toggle > summary::marker{content:"";}
.site-header__toggle > summary:hover{background:var(--mist);}
/* fill:none;stroke:currentColor - matching mn-btn__icon's own treatment
   (home-page/page.css section 7) so every icon on the site is drawn the
   same way. Missing this once was a real, silent bug: the hamburger's
   three open horizontal path segments have no closed area, so left at
   the SVG default (fill, no stroke) they rendered as an invisible
   44x44px button - caught in the browser at 390px, not assumed. */
.site-header__toggle svg{
  inline-size:24px;block-size:24px;
  fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}
.site-header__toggle .icon-close{display:none;}
.site-header__toggle[open] .icon-menu{display:none;}
.site-header__toggle[open] .icon-close{display:block;}

/* ---------------------------------------------------------------------
   Primary nav

   Hidden by default below the desktop breakpoint - not with `hidden` or
   JS, with the sibling combinator reading the toggle's own native
   [open] state, so this still works with JavaScript off (see the toggle
   comment above). At and above the desktop breakpoint the rule after it
   simply wins on source order and the open/closed state stops mattering.
   --------------------------------------------------------------------- */
.site-header__toggle:not([open]) ~ .site-nav,
.site-header__toggle:not([open]) ~ .site-header__search{
  display:none;
}
.site-nav{
  order:4;
  inline-size:100%;
  padding-block:12px 18px;
  border-block-start:1px solid var(--line);
}
.site-nav__list{
  display:flex;
  flex-direction:column;
  list-style:none;
  margin:0;padding:0;
}
.site-nav__list > li{
  border-block-end:1px solid var(--line);
}
.site-nav__list > li:last-child{border-block-end:0;}

/* The plain links - About Us, Contact Us - and the summary that opens
   each dropdown share this rule so all four top-level items sit at one
   visual weight, exactly as asked ("apart from this, About us and
   Contact us will appear on the header" - four peer items, not three
   links and one differently-weighted control). */
.site-nav__link,
.site-nav__list > li > details > summary{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:8px;
  padding-block:14px;
  font-size:15.5px;font-weight:500;
  color:var(--ink);
  text-decoration:none;
  cursor:pointer;
  list-style:none;
}
.site-nav__list > li > details > summary::-webkit-details-marker{display:none;}
.site-nav__list > li > details > summary::marker{content:"";}
.site-nav__link:hover,
.site-nav__link:focus-visible,
.site-nav__link[aria-current="page"],
.site-nav__list > li > details[open] > summary,
.site-nav__list > li > details > summary:hover,
.site-nav__list > li > details > summary:focus-visible{color:var(--ocean);}

.site-nav__chevron{
  flex:none;
  inline-size:14px;block-size:14px;
  fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
  color:var(--slate);
}
@media (prefers-reduced-motion: no-preference){
  .site-nav__chevron{transition:transform .2s var(--ease);}
}
.site-nav__list > li > details[open] > summary .site-nav__chevron{
  transform:rotate(180deg);
}

/* ---------------------------------------------------------------------
   Industry Reports only, 2026-09-04 (requested) - a real link plus a
   separate disclosure button, not <details>/<summary>. A single summary
   is one native click target that always toggles; it can't also
   navigate anywhere, so once "Industry Reports" itself needed to be a
   real link, the label and the disclosure control had to become two
   elements. .site-nav__row lays them out as one visual row - the link
   takes the remaining space (flex:1) so the button sits flush at the
   end, the same position the chevron held inside the old summary.
   assets/js/modules/site-header.js ("initLinkedDropdown") drives this
   button's open/close state via aria-expanded and the panel's `hidden`
   attribute; see header/NOTES.md, "Industry Reports becomes a real
   link" for the full account, including why the panel ships visible
   (not `hidden`) in the HTML itself - with JavaScript off, the sector
   links stay reachable rather than being permanently collapsed. -------
   --------------------------------------------------------------------- */
.site-nav__row{
  display:flex;
  align-items:center;
}
.site-nav__row .site-nav__link{flex:1;}
.site-nav__panel-toggle{
  display:flex;
  flex:none;
  align-items:center;
  justify-content:center;
  inline-size:36px;block-size:36px;
  border:0;
  background:transparent;
  border-radius:50%;
  color:var(--slate);
  cursor:pointer;
}
.site-nav__panel-toggle:hover,
.site-nav__panel-toggle:focus-visible,
.site-nav__panel-toggle[aria-expanded="true"]{
  background:var(--mist);
  color:var(--ocean);
}
.site-nav__panel-toggle[aria-expanded="true"] .site-nav__chevron{
  transform:rotate(180deg);
}

/* ---------------------------------------------------------------------
   The dropdown panels. Resources is still <details>/<summary> - plain
   show/hide, native, works with JavaScript off. Industry Reports' own
   panel (immediately above) is the one exception, for the reason
   explained there; both panels share this same visual styling either
   way. The two are independent (opening one does not require closing
   the other for the site to work correctly); assets/js/modules/
   site-header.js adds closing-the-sibling and Escape-to-close as
   quality-of-life on top, not as something either panel depends on.
   --------------------------------------------------------------------- */
.site-nav__panel{
  list-style:none;
  margin:0;
  padding:2px 0 10px 18px;
}
.site-nav__panel > li + li{margin-block-start:2px;}
.site-nav__panel a{
  display:block;
  padding-block:9px;
  font-size:14.5px;font-weight:400;
  color:var(--slate);
  text-decoration:none;
  border-radius:2px;
}
.site-nav__panel a:hover{color:var(--ocean);}

/* ---------------------------------------------------------------------
   Search - kept (not asked to be removed), restyled from the vendor
   chrome's full-width placeholder box to a compact field. Always in the
   DOM and always visible: CLAUDE.md requires every form to submit
   without JavaScript, so this cannot default to a JS-revealed state.
   action, method and the input's name="search_form" are unchanged from
   the vendor markup - see header/NOTES.md, "Functional inventory".
   --------------------------------------------------------------------- */
.site-header__search{
  order:3;
  inline-size:100%;
  margin-block-start:4px;
}
.site-header__search-field{
  display:flex;
  align-items:center;
  gap:8px;
  padding-inline:14px;
  border:1px solid var(--rule);
  border-radius:999px;
  background:var(--mist);
}
.site-header__search-field:focus-within{
  border-color:var(--teal);
  background:var(--tint);
}
.site-header__search-field svg{
  flex:none;
  inline-size:17px;block-size:17px;
  fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
  color:var(--slate);
}
.site-header__search-field input{
  flex:1;
  min-inline-size:0;
  padding-block:11px;
  border:0;
  background:transparent;
  font-family:var(--ui);
  font-size:14.5px;
  color:var(--ink);
}
.site-header__search-field input::placeholder{color:var(--slate);opacity:.75;}
.site-header__search-field input:focus{outline:none;}
.site-header__search-field button{
  flex:none;
  display:flex;
  padding:6px;
  border:0;background:transparent;
  color:var(--ocean);
  cursor:pointer;
  border-radius:50%;
}
.site-header__search-field button:hover{background:var(--tint);}

/* Requested: a clear way to close the expanded field. Hidden at rest -
   showing a close control before there's anything to close would just
   be a second, confusing icon next to the actual search button - and
   revealed by the same :focus-within state that drives the expansion
   itself, so it can't fall out of sync with whether the field is
   actually open. Slate rather than the submit button's ocean tint on
   purpose, so the two don't read as equally-weighted actions: search is
   the primary one, close is secondary.

   Qualified as .site-header__search-field .site-header__search-close,
   not the bare class alone - .site-header__search-field button (above)
   is one class plus one type selector, which outranks a single bare
   class. Confirmed in the browser: with the bare class this rule lost
   that specificity contest and the button stayed visible at rest,
   contradicting the "hidden at rest" comment above it. */
.site-header__search-field .site-header__search-close{
  display:none;
  color:var(--slate);
}
.site-header__search-close:hover{color:var(--ink);background:var(--mist);}
.site-header__search:focus-within .site-header__search-close{
  display:flex;
}

/* The live-search suggestion panel, filled by js/redesign/site-header.js
   with one <ul> of links or a single <p> when nothing matched.

   It deliberately does NOT reuse the vendor markup's id="search_result"
   class="search_result_data" (2026-09-14). customstyle.css still carries
   that class's rules for the old header - a hardcoded #fff/13px/5px
   radius, and `@media(min-width:767px) and (max-width:991px){
   margin-left:30px}` which, measured at a 802px viewport, put this panel
   at x=54 against a field at x=24 and 6px past the viewport's right edge.

   .site-header__search MUST be positioned (added 2026-09-11 during the
   Laravel port). This panel is position:absolute with inline-size:100%,
   and with a static parent it resolved against .site-header instead:
   measured at a 1531px viewport, the panel came out 1531px wide starting
   at x=1265, i.e. 1265px of horizontal overflow the moment a result
   appeared. */
.site-header__search{position:relative;}
.site-header__suggest{
  position:absolute;
  z-index:5;
  inline-size:100%;
  /* The field grows to 340px on focus and further still on wide rows, so
     the panel would otherwise be as narrow as the collapsed field on a
     phone and title-truncatingly wide on none of them. min- gives it room
     to be readable; max- keeps it inside the viewport at 320px. */
  min-inline-size:min(320px, 100%);
  max-block-size:min(60vh, 420px);
  margin-block-start:6px;
  overflow-y:auto;
  overscroll-behavior:contain;
  background:var(--paper);
  border:1px solid var(--rule);
  border-radius:var(--r);
  box-shadow:0 12px 28px rgb(4 11 52 / 12%);
}
.site-header__suggest-list{
  margin:0;
  padding:6px;
  list-style:none;
}
/* The whole row is the target, not the words inside it - a suggestion
   list where only the text is clickable reads as broken. */
.site-header__suggest-list a{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:12px;
  padding:9px 10px;
  border-radius:calc(var(--r) - 4px);
  font-family:var(--ui);
  font-size:14px;
  line-height:1.4;
  color:var(--ink);
  text-decoration:none;
}
.site-header__suggest-list a:hover,
.site-header__suggest-list a:focus-visible{
  background:var(--tint);
  color:var(--ocean);
}
/* Which of the two sources a row came from. Kept quiet and to one side:
   it disambiguates, it is not the thing being chosen. */
.site-header__suggest-kind{
  flex:none;
  font-size:11px;
  letter-spacing:.04em;
  text-transform:uppercase;
  color:var(--slate);
}
.site-header__suggest-empty{
  margin:0;
  padding:14px 16px;
  font-family:var(--ui);
  font-size:14px;
  color:var(--slate);
}

/* ---------------------------------------------------------------------
   Desktop, >=1024px

   One row: brand, nav, search, no toggle. 1024 rather than a narrower
   line because four top-level items (one of them "Industry Reports" plus
   a chevron) plus the search field need real room - measured in the
   browser at 1024/1152/1280, not assumed; see header/NOTES.md.
   --------------------------------------------------------------------- */
@media (min-width: 1024px){
  .site-header__toggle{display:none;}
  /* The toggle's [open] state stops mattering at this width - both
     collapsible pieces are forced visible regardless of it, which is
     simpler and more robust than trying to keep a phone-only control's
     attribute in sync with a desktop viewport.

     display:block, NOT display:flex - neither wrapper needs to be a
     flex container itself; .site-nav__list and .site-header__search-field
     already run their own flex row inside it. Setting display:flex here
     briefly made .site-header__search a flex container, which switched
     its <form> child's sizing from "block fills the 220px parent" to
     "flex item sized to its own content" and the field overflowed past
     var(--mn-measure) - caught in the browser at 1280px, not assumed. */
  .site-header__toggle:not([open]) ~ .site-nav,
  .site-header__toggle:not([open]) ~ .site-header__search{
    display:block;
  }
  /* Visual order swaps from mobile's brand/search/nav to brand/nav/
     search: search reads as a persistent utility at the row's end once
     there is room for it beside the links, rather than announcing
     itself above them the way it does when it is the first thing in a
     newly opened mobile panel. */
  .site-nav{
    order:2;
    inline-size:auto;
    flex:1;
    padding-block:0;
    border-block-start:0;
  }
  /* justify-content:flex-end - .site-nav is flex:1, so on any screen wider
     than the links+search actually need, the list's own box stretches
     past its content. Left-aligned (the default), that left 476px of
     dead air between "Contact Us" and the search field at 1920px -
     measured in the browser, not assumed. Right-aligning the list closes
     that gap so the links sit flush against the search field they
     belong next to, moving the breathing room to sit next to the brand
     mark instead, where it reads as intentional. */
  .site-nav__list{
    flex-direction:row;
    align-items:center;
    justify-content:flex-end;
    gap:clamp(4px,1.6vw,28px);
  }
  /* Both declarations belong to the same item: no bottom hairline (that
     was the mobile stacked-list divider) and a positioned box, since
     its own dropdown panel below is placed absolutely against it. */
  .site-nav__list > li{
    border-block-end:0;
    position:relative;
  }
  .site-nav__link,
  .site-nav__list > li > details > summary{padding-block:8px;}

  .site-nav__list > li > details{position:static;}
  .site-nav__panel{
    position:absolute;
    inset-block-start:calc(100% + 14px);
    inset-inline-start:0;
    z-index:30;
    min-inline-size:15rem;
    padding:10px;
    background:var(--paper);
    border:1px solid var(--rule);
    border-radius:var(--r);
    box-shadow:0 20px 44px rgb(4 11 52 / 14%);
  }
  .site-nav__panel a{border-radius:var(--r);padding-inline:10px;}

  /* Industry Reports only (12 sector links) - two columns, requested,
     so the panel reads as a short wide block instead of a long
     scroll-y single column. grid-auto-flow:column with an explicit row
     count is what decides the column count: 7 rows fits 12 items into
     exactly 2 columns (column-major, so column one runs top-to-bottom
     before column two starts) with the header's own list-item order
     otherwise unchanged. Resources (3 links) is not targeted - two
     columns of one-to-two links each would be worse, not better.

     :not([hidden]) is load-bearing, not defensive. Since 2026-09-04
     this panel is shown/hidden via the `hidden` attribute (site-header.js,
     "initLinkedDropdown"), not <details> open/closed - and an ID
     selector's plain `display:grid` outranks the UA stylesheet's
     `[hidden]{display:none}` regardless of specificity, since UA rules
     are the lowest-priority origin in the cascade. Without this
     exclusion the panel would render every time regardless of `hidden`.
     This went unnoticed on the two pages that already carry this header
     only because they still load Bootstrap, whose own `[hidden]` rule
     carries !important and happened to win anyway - caught for real
     building faq/index.html, which loads no vendor CSS to mask it.
     See header/NOTES.md, "The [hidden] specificity bug". */
  #nav-industry-reports:not([hidden]){
    display:grid;
    grid-auto-flow:column;
    grid-template-rows:repeat(7,auto);
    column-gap:4px;
    min-inline-size:30rem;
  }

  .site-header__search{
    order:3;
    inline-size:auto;
    max-inline-size:220px;
    margin-block-start:0;
  }
  /* Requested: clicking into search should expand it - 220px fits the
     placeholder but not much typed text. :focus-within, not JS: this is
     a CSS state already computed from real user focus, no listener
     needed to keep it in sync, and it still expands with the input
     focused by keyboard (Tab), not just a click.

     flex-grow is what does the growing, not a bigger width/max-width on
     its own. .site-header__search is flex-grow:0 at rest - it was only
     ever meant to sit at its content's size - and .site-nav is flex:1
     with a 0% basis, so .site-nav absorbs 100% of any spare row width by
     design (that's this component's second bug fix, above). Raising this
     item's own max-width without also giving it grow supplies a higher
     ceiling but no force toward it, so nothing changes: confirmed in the
     browser, not assumed - a real click produced no visible movement.
     Forcing a bigger flex-basis instead (a definite inline-size) was
     tried next and made it WORSE: with no min-inline-size override this
     item's automatic minimum is its own min-content size, and a forced
     basis bigger than the row can spare gets shrunk straight back down
     to that floor, landing on the exact same rendered width as before -
     also confirmed in the browser. flex-grow:1 avoids both: it competes
     for the row's spare space on equal terms with .site-nav, and
     max-inline-size caps how far it's allowed to win that contest -
     see the rule right below for the state where that cap is lifted
     entirely instead. */
  .site-header__search:focus-within{
    flex-grow:1;
    max-inline-size:340px;
  }
  /* Requested next: expand it further still, all the way to the brand
     mark, by hiding the nav links rather than sharing the row with them.
     :has() on the bar, not a class toggled by JS - this is a pure
     consequence of focus, and CLAUDE.md already treats :has() as
     ordinary modern CSS (see "Wrapper elimination" at the top of this
     project's standards). With .site-nav removed from the row entirely,
     .site-header__search is left competing for space with nothing but
     the brand mark, so flex-grow:1 and no max-inline-size cap take it
     all the way there - no fixed width to guess at and re-measure if the
     brand's own size ever changes. */
  .site-header__bar:has(.site-header__search:focus-within) .site-nav{
    display:none;
  }
  .site-header__bar:has(.site-header__search:focus-within) .site-header__search{
    max-inline-size:none;
  }
}

/* =====================================================================
   2. Site footer

   Replaces the vendor .footer chrome (Bootstrap .container/.row/
   col-md-3 grid, Font Awesome <i> icons, an <a href="javascript:void(0)">
   wrapping a physical address, a duplicated class attribute, an
   href="#" used purely as a visual "|" divider) with a single grid,
   real lists, and inline SVG. See footer/NOTES.md for the full design
   and functional-inventory record - this is the pointer, same relation
   header/NOTES.md has to home-page/NOTES.md's own header entry.
   ===================================================================== */

/* ---------------------------------------------------------------------
   The footer itself IS the grid container - no wrapper.

   padding-inline:max(gutter, (100% - measure) / 2) does the same job
   .site-header__bar's wrapper div did (paint full-bleed, inset the
   content to this site's shared measure) without a second element:
   grid doesn't have flexbox's "these children must wrap together as one
   row" problem, which was the header bar's actual justification, so
   nothing here needs it. One CSS declaration standing in for a whole
   element is exactly the trade this project's wrapper-elimination
   guidance (CLAUDE.md) asks for. --mn-gutter/--mn-measure are the same
   tokens the header's own container resolves, so this footer's left and
   right edges land on the header's, not a third, independently-guessed
   pair of numbers.
   --------------------------------------------------------------------- */
/* The floor is 280px, not a rounder 220-240px, for one reason: at this
   grid's widest possible content box (--mn-measure, 1560px) with the
   column-gap capped at its own max (48px), 220px was small enough to let a
   FIFTH auto-fit track exist alongside the four real columns (About, Quick
   Links, Key Industries, Contact) - 5*220px+4*48px = 1292px, comfortably
   under 1560px. That phantom track never collapsed, because
   .site-footer__bottom below spans grid-column:1/-1 and per spec an
   auto-fit track collapses only if NOTHING is placed into or spans across
   it - the copyright bar's own full-width span was keeping the fifth track
   alive. Net effect: four real columns rendered at equal width to each
   other, but confined to 4/5 of the row, leaving the right edge short and
   the whole block reading as unevenly spread rather than filling it -
   reported 2026-09-11, this is that bug, not a token/gap miscalculation.

   280px is chosen so a fifth track can never fit even at the maximum
   content width: 5*280px+4*48px = 1592px > 1560px, always false, at every
   viewport - the four real columns are the only ones that can ever exist,
   so grid-column:1/-1 on the bar below is safe again. If --mn-measure or
   this gap ever change, re-run this arithmetic - see footer/NOTES.md. */
.site-footer{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:clamp(32px,5vw,56px) clamp(24px,4vw,48px);
  padding-block:clamp(48px,6vw,72px) clamp(28px,4vw,40px);
  padding-inline:max(var(--mn-gutter),calc((100% - var(--mn-measure)) / 2));
  background:var(--ink);
  color:var(--on-dark);
  /* Requested 2026-09-11: match the font used for headings/paragraphs,
     not the UI chrome. Restated literally rather than var(--text) -
     --text is scoped per page (Source Serif 4 on most pages, Merriweather
     on request-sample-page, see that page's page.css) and this footer is
     one shared component embedded on all of them. Pinning the literal
     stack keeps the footer's own type identical everywhere, instead of
     silently changing font depending on which page it's dropped into. */
  font-family:'Source Serif 4',Georgia,'Times New Roman',serif;
}
/* The one unbreakable string in this component (the postal address) -
   carried over from the vendor patch this replaces, which flagged it as
   "the only source of horizontal overflow in the chrome." Still true of
   the new markup, so the fix travels with it. */
.site-footer a,
.site-footer p,
.site-footer dd{overflow-wrap:anywhere;}

.site-footer__heading{
  margin:0 0 16px;
  font-size:14px;
  font-weight:600;
  letter-spacing:.04em;
  text-transform:uppercase;
  color:var(--on-dark-hi);
}

.site-footer__lede{
  margin:0 0 20px;
  max-inline-size:34ch;
  font-size:14.5px;
  line-height:1.6;
}

.site-footer__social{
  display:flex;
  gap:10px;
  list-style:none;
  margin:0;padding:0;
}
.site-footer__social a{
  display:flex;
  align-items:center;
  justify-content:center;
  inline-size:36px;block-size:36px;
  border-radius:50%;
  background:rgb(255 255 255 / 8%);
  color:var(--on-dark-hi);
}
.site-footer__social svg{
  inline-size:16px;block-size:16px;
  fill:currentColor;
}
.site-footer__social a:hover,
.site-footer__social a:focus-visible{
  background:var(--teal);
  color:var(--paper);
}
@media (prefers-reduced-motion: no-preference){
  .site-footer__social a{transition:background .18s var(--ease),color .18s var(--ease);}
}

.site-footer__col ul{
  display:flex;
  flex-direction:column;
  gap:11px;
  list-style:none;
  margin:0;padding:0;
}
/* .site-footer__heading's own margin-block-end (16px) STACKS with this
   list's gap (11px) when the heading is the ul's first li - flex gaps
   don't collapse with sibling margins the way block margins do. Trimmed
   here so heading-to-first-link spacing is 16px total, matching the
   plain-<p> heading columns (About, Contact), not 27px - see
   footer/NOTES.md, "Quick Links heading gap trimmed to 16px". */
.site-footer__col li.site-footer__heading{
  margin-block-end:5px;
}
.site-footer__col a,
.site-footer__contact dd a{
  color:var(--on-dark);
  text-decoration:none;
  font-size:14.5px;
}
.site-footer__col a:hover,
.site-footer__col a:focus-visible,
.site-footer__contact dd a:hover,
.site-footer__contact dd a:focus-visible{
  color:var(--aqua);
  text-decoration:underline;
}

/* ---------------------------------------------------------------------
   Contact - a <dl>, not stacked <p> tags: three genuine label/value
   pairs (Email, Phone, Corporate Office), exactly what CLAUDE.md's
   document-structure rule asks a <dl> for. Flex column rather than a
   two-column grid - dt/dd already alternate in the DOM in the right
   reading order, so stacking them is the whole layout; a grid would be
   solving a problem that isn't there.
   --------------------------------------------------------------------- */
.site-footer__contact dl{
  display:flex;
  flex-direction:column;
  margin:0;
}
.site-footer__contact dt{
  display:flex;
  align-items:center;
  gap:8px;
  margin-block:18px 6px;
  font-size:12.5px;
  font-weight:600;
  letter-spacing:.04em;
  text-transform:uppercase;
  color:var(--on-dark-hi);
}
.site-footer__contact dt:first-child{margin-block-start:0;}
.site-footer__contact dt svg{
  flex:none;
  inline-size:15px;block-size:15px;
  fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}
.site-footer__contact dd{
  margin:0;
  font-size:14.5px;
}

/* Requested 2026-09-10: centered, now that Privacy Policy/Terms of Service
   (formerly .site-footer__legal here) moved into Quick Links and this bar
   holds only the copyright - see footer/NOTES.md, "Legal links removed
   from the bottom bar". flex-wrap/gap kept: overflow-wrap:anywhere (above)
   handles mid-word breaks, but this still lets the line wrap at a space on
   very narrow viewports without kept. */
.site-footer__bottom{
  grid-column:1 / -1;
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:center;
  gap:12px 24px;
  margin-block-start:clamp(28px,4vw,44px);
  padding-block-start:24px;
  border-block-start:1px solid var(--dark-rule);
}
.site-footer__copyright{
  margin:0;
  font-size:13.5px;
}

/* =====================================================================
   SHARED ENQUIRY POPUP  (.mn-enquiry)

   Added 2026-09-10. The redesign of the site's own #myModal /
   #requestCallForm popup - ONE form reused by many calls to action, with
   the heading swapped per button. Markup and functional inventory:
   enquiry-popup/enquiry-popup-partial.html and enquiry-popup/NOTES.md.

   WHY IT IS IN THIS FILE. It is a cross-page component in exactly the
   sense CLAUDE.md means: the site already opens this one popup from
   calls to action on the report description page, the pricing page and
   the home page. It belongs beside the header and footer, not forked
   into a page stylesheet.

   NO @layer, for the reason this file already gives elsewhere: the app
   still loads five unlayered legacy stylesheets, and unlayered CSS beats
   every layer. Source order is the cascade here.

   COLOURS ARE SCOPED, NOT GLOBAL. The four values below that tokens.css
   does not carry are declared on the block itself rather than added to
   :root - the same call .mn-practice (--mnp-*) and the voices band
   (--voices-*) already made on the home page. Nothing outside this
   component can see them, so no delivered page's rendering moves.
   ===================================================================== */

.mn-enquiry{
  /* --slate (#41505E) is the body-copy slate and reads as a heavy rule on
     a form field. #67788A is the muted slate the report description page
     and the request-sample page both already use; it measures 4.54:1 on
     white, which clears both AA for the hint text and SC 1.4.11's 3:1 for
     a field boundary, where tokens.css's --rule (1.48:1) does not. */
  --mn-enq-muted:#67788A;
  /* .mn-btn--solid's ramp from home-page/assets/css/page.css, copied not
     invented: the popup's submit is the same primary button as the CTA
     that opened it. White on the lightest stop (#0A7F91) is 4.6:1. */
  --mn-enq-solid:linear-gradient(135deg,#0A7F91 0%,#026C86 48%,#004E79 100%);
  --mn-enq-solid-hover:linear-gradient(135deg,#0C8EA2 0%,#037795 48%,#005887 100%);
  --mn-enq-invalid:#B4231C;

  /* THE CONTAINER. The form's two-column threshold is a question about
     the CARD's width, not the viewport's - this popup is 620px on a
     desktop and a near-full-bleed sheet on a phone, and the fields should
     answer to that rather than to a media query that knows nothing about
     which. */
  container-type:inline-size;

  /* Two rows: the header line, then the form. Done with grid placement
     rather than a header <div>, so the close button and the title sit on
     one row without an element wrapping them.

     BOTH ROWS ARE `auto` (2026-09-11, requested: "there will be no scroll
     on the popup enquiry form"). The second row was minmax(0,1fr), which
     is what let the form become its own scroll box inside a capped card.
     The card now takes the height its content asks for and the whole form
     is on screen at once.

     `display` IS SET ON [open] BELOW, NOT HERE, and that is not a style
     choice. Author CSS beats the UA sheet whatever its specificity, so a
     bare `display:grid` overrides `dialog:not([open]){display:none}` and
     the closed dialog renders into the page - invisible at opacity 0, but
     571x489 of layout after the footer with thirteen tabbable controls in
     it. The grid template can live here because it is inert until the
     element is actually a grid. */
  grid-template-columns:minmax(0,1fr) auto;
  grid-template-rows:auto auto;
  gap:0 14px;

  /* 620 is the width at which the six controls read as two comfortable
     columns; below that the card is the viewport less one gutter, so the
     inset never switches off - it narrows continuously from 1920 down to
     320. --mn-gutter is the site's single gutter token. */
  inline-size:min(620px,100% - var(--mn-gutter) * 2);

  /* NOT a design height - a reflow guard. The card is content-sized, and
     on every width in the ladder from 360 up the content is shorter than
     this, so nothing scrolls. It only bites on a viewport genuinely
     shorter than the form (a 568px-tall phone, a landscape phone, 200%
     zoom), where WCAG 2.2 SC 1.4.10 requires the content stay reachable -
     so `overflow:auto` below moves the WHOLE card, header included,
     rather than clipping fields no one can get to. See
     enquiry-popup/NOTES.md. */
  max-block-size:calc(100vh - 32px);
  max-block-size:calc(100svh - 32px);
  margin:auto;
  padding:clamp(20px,3vw,32px);

  color:var(--ink);
  background:var(--paper);
  border:0;
  border-radius:calc(var(--r) * 2);
  box-shadow:0 24px 64px rgb(4 11 52 / 28%);
  overflow:auto;

  /* A <dialog> goes from display:none to display:block, so an entrance
     needs the discrete properties allowed and a @starting-style to
     animate from. A browser supporting neither simply gets the open
     state, which is the correct one. */
  opacity:1;
  translate:0 0;
  transition:
    opacity .24s var(--ease),
    translate .24s var(--ease),
    overlay .24s allow-discrete,
    display .24s allow-discrete;
}
/* The open state, and the only place `display` is set - see above. */
.mn-enquiry[open]{display:grid;}
@starting-style{
  .mn-enquiry[open]{opacity:0;translate:0 12px;}
}
/* The state the exit transition runs towards. `display` is left to the UA
   sheet here; `transition-behavior:allow-discrete` on the shorthand above
   is what holds the box visible for the 240ms it takes to get here. */
.mn-enquiry:not([open]){opacity:0;translate:0 8px;}

.mn-enquiry::backdrop{
  background:rgb(4 11 52 / 62%);
  backdrop-filter:blur(3px);
}

/* One focus treatment for everything the popup owns. --focus is the
   site's token; the fallback is there because this component can be
   dropped into a page whose stylesheet has not declared one. */
.mn-enquiry :focus-visible{
  outline:3px solid var(--focus,var(--teal));
  outline-offset:2px;
}

.mn-enquiry__close{
  grid-area:1 / 2;
  display:flex;
  align-items:center;
  justify-content:center;
  /* 40px, so the target clears 24x24 with room to spare on touch. */
  inline-size:40px;block-size:40px;
  margin-block-start:-4px;
  padding:0;
  color:var(--slate);
  background:var(--mist);
  border:1px solid var(--line);
  border-radius:50%;
  cursor:pointer;
  transition:background-color .2s var(--ease),color .2s var(--ease);
}
@media (hover:hover){
  .mn-enquiry__close:hover{background:var(--ground);color:var(--paper);border-color:var(--ground);}
}
.mn-enquiry__close svg{
  inline-size:18px;block-size:18px;
  fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;
}

/* THE ONE ELEMENT THAT CHANGES PER CALL TO ACTION. It is sized to hold
   two lines of the longest heading in use ("Request a supplier risk
   briefing") at 320px without the form's first field leaving the card. */
.mn-enquiry__title{
  grid-area:1 / 1;
  align-self:center;
  margin:0;
  font-family:var(--text);
  font-size:clamp(19px,2.2vw,26px);
  font-weight:600;
  line-height:1.2;
  letter-spacing:-.015em;
  color:var(--ink);
  text-wrap:balance;
}

/* --------------------------------------------------------------------
   THE NARROW ARRANGEMENT IS THE BASE, and the container query below is
   what widens it - CLAUDE.md's order of work, and the reason the two
   states tile exactly with no sub-pixel window between them.

   Two columns are declared at every width; what changes is WHICH fields
   use them. Narrow, only the name pair does - a column is ~138px at 320,
   which the country group cannot use (a 92px code box would leave 46px
   for the number), so everything else takes the full measure.

   2026-09-11: the narrow case used to be ONE column for all six
   controls, and the card was 79px too tall to fit a 320 x 568 phone.
   Pairing the two name fields takes a whole 64px row and its gap out.
   The rule is written as "everything except the pair spans", so adding a
   field later needs no new selector.
   -------------------------------------------------------------------- */
.mn-enquiry__form{
  grid-column:1 / -1;
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  align-content:start;
  /* The narrow spacing. Still above the 8px the scale bottoms out at,
     and a row gap separates two labelled controls rather than grouping
     them, so it is the cheapest height on the card to buy back. */
  gap:10px 12px;
  margin-block-start:12px;
  font-family:var(--ui);
  /* NO overflow, NO scrollbar-gutter, NO min-block-size:0 (2026-09-11,
     requested). Those three made the form a scroll box inside a capped
     card: a scrollbar strip reserved on every screen whether or not it
     was used, and on a short viewport a form you had to scroll while the
     submit button sat out of sight. The form is ordinary content now and
     the card grows to hold it. */
}

.mn-enquiry__field:not(.mn-enquiry__field--pair),
.mn-enquiry__form .cf-turnstile,
.mn-enquiry__submit,
.mn-enquiry__terms{grid-column:1 / -1;}

/* The Turnstile widget. 65px is its own rendered height, reserved up front
   so the card does not jump when the iframe arrives - it renders
   asynchronously and the dialog is already on screen by then.

   Selected by Cloudflare's own class rather than a wrapper: the element is
   emitted by layout/_turnstile.blade.php, which renders NOTHING when
   Turnstile is switched off, so a wrapper here would leave an empty 65px
   block in the form on those boxes. The design handoff ships without this
   element at all - a third-party fetch is the application's, not the
   design's. */
.mn-enquiry__form .cf-turnstile{min-block-size:65px;}

/* A CONTAINER query on purpose - the question is how wide the CARD is,
   not the window - and it resolves against the card's CONTENT box, so
   the switch lands about 32px later in card terms.

   430, NOT the 460 this started at. At 520 of viewport the card stops
   being a full-bleed sheet and becomes a centred panel, which makes it
   NARROWER than it was at 519: 519 wide, then 482. With the threshold at
   460 the form went two columns at 519, back to the narrow arrangement
   at 520, and two columns again at 538 - a shape that toggled twice
   while the window only grew. 430 clears the 442 the card has at 520, so
   the arrangement changes exactly once, at 462 of viewport, and never
   goes back. The country group still gets 92px of code box and 115 of
   number at that width.

   The card-width jump at 520 is itself pre-existing and is NOT fixed
   here - see enquiry-popup/NOTES.md §13. */
@container (min-width:430px){
  .mn-enquiry__form{gap:12px 16px;margin-block-start:clamp(16px,2.4vw,22px);}
  .mn-enquiry__field:not(.mn-enquiry__field--wide){grid-column:auto;}
}

/* align-content, not the default stretch, so a field that grows for any
   reason does not stretch its row partner's control to match it. */
.mn-enquiry__field{display:grid;align-content:start;gap:4px;margin:0;}

.mn-enquiry__field label{
  display:flex;
  align-items:baseline;
  gap:8px;
  font-size:12.5px;
  font-weight:600;
  line-height:1.25;
  color:var(--ink);
}

/* .mn-enquiry__opt and .mn-enquiry__hint WERE HERE and are gone with the
   markup they styled (2026-09-11, requested: drop the email hint and the
   OPTIONAL mark). Nothing in the partial carries either class any more, so
   the rules would be dead weight in a shared sheet. If the mark comes
   back, so does its rule - see enquiry-popup/NOTES.md §13d. The label's
   `display:flex` and `gap:8px` above are what a second inline element in
   a label needs, and they stay for that reason. */

.mn-enquiry__form :is(input,select,textarea){
  inline-size:100%;
  min-block-size:44px;
  padding:10px 12px;
  font:inherit;
  font-size:15px;
  color:var(--ink);
  background:var(--paper);
  border:1px solid var(--mn-enq-muted);
  border-radius:var(--r);
  transition:border-color .18s var(--ease),box-shadow .18s var(--ease);
}
.mn-enquiry__form textarea{min-block-size:76px;line-height:1.45;resize:vertical;}
.mn-enquiry__form ::placeholder{color:var(--mn-enq-muted);opacity:1;}
.mn-enquiry__form :is(input,select,textarea):focus{
  border-color:var(--teal);
  box-shadow:0 0 0 3px rgb(11 143 163 / 16%);
}

/* The select keeps an affordance rather than losing it to
   appearance:none - the chevron is drawn back in as a background image.
   background-position has no logical-property equivalent, so `right` is
   one of the few genuinely physical values in this block. */
.mn-enquiry__form select{
  appearance:none;
  padding-inline-end:38px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2341505E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 13px center;
  background-size:16px 16px;
}

/* --------------------------------------------------------------------
   The telephone field: two controls in one bordered group, the country
   code on the leading edge. No wrapper element - the field's own grid
   does it. Lifted from request-sample-page/assets/css/page.css, where
   the same decision is documented in full.
   -------------------------------------------------------------------- */
.mn-enquiry__field--tel{
  grid-template-columns:clamp(92px,32%,116px) minmax(0,1fr);
  column-gap:0;
}
.mn-enquiry__field--tel > label:not(.mn-vh){grid-column:1 / -1;}
/* Both sit in the stacking context so whichever is focused paints its
   border and ring over its neighbour's edge rather than under it. */
.mn-enquiry__field--tel :is(select,input){position:relative;}
.mn-enquiry__field--tel :is(select,input):focus{z-index:1;}
.mn-enquiry__field--tel select{
  padding-inline:10px 24px;
  background-position:right 7px center;
  background-size:14px 14px;
  border-start-end-radius:0;
  border-end-end-radius:0;
  margin-inline-end:-1px;
  text-overflow:ellipsis;
}
.mn-enquiry__field--tel input{
  border-start-start-radius:0;
  border-end-start-radius:0;
}

/* :user-invalid, not :invalid - an untouched required field is not red
   before anyone has typed. Colour is not the only signal: the border
   weight changes too. */
.mn-enquiry__form :is(input,select,textarea):user-invalid{
  border-width:2px;
  border-color:var(--mn-enq-invalid);
}

.mn-enquiry__submit{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:10px;
  min-block-size:50px;
  margin-block-start:4px;
  padding:13px clamp(16px,2.2vw,28px);
  font:inherit;
  font-size:16px;
  font-weight:600;
  color:var(--paper);
  background:var(--mn-enq-solid);
  border:1px solid transparent;
  border-radius:var(--r);
  box-shadow:0 8px 22px rgb(0 78 121 / 28%);
  cursor:pointer;
  transition:background .2s var(--ease),transform .2s var(--ease),box-shadow .2s var(--ease);
}
@media (hover:hover){
  .mn-enquiry__submit:hover{
    background:var(--mn-enq-solid-hover);
    transform:translateY(-1px);
    box-shadow:0 12px 26px rgb(0 78 121 / 32%);
  }
}
.mn-enquiry__submit:active{transform:translateY(0);box-shadow:0 4px 12px rgb(0 78 121 / 30%);}
.mn-enquiry__submit-icon{
  flex:none;
  inline-size:1.05em;block-size:1.05em;
  fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;
  color:var(--aqua);
}
/* Every click inside the button resolves to the button. */
.mn-enquiry__submit > *{pointer-events:none;}

.mn-enquiry__terms{
  margin:0;
  font-size:12px;
  line-height:1.5;
  color:var(--mn-enq-muted);
}
.mn-enquiry__terms a{color:var(--ocean);}

/* THE VENDOR SHEET SIZES EVERY LINK ON THE SITE. style.css line ~1 of its
   minified body carries `.cart-border p,a,ul.top-right-login i{font-size:1rem}`
   - an unlayered element rule, so it beats any @layer and, more to the
   point, it STOPS INHERITANCE: the two terms links rendered at 16px
   inside a 12px paragraph, three lines instead of two, on every page that
   loads the app's CSS. The component preview looked right because it does
   not load that sheet. Found 2026-09-11 while measuring the card height.

   font-size:inherit is the whole fix, and it is scoped to the popup so it
   changes nothing else on the page. Anything else we add inside this
   component that contains a link is defended by the same line. */
.mn-enquiry a{font-size:inherit;}

/* Long strings in a 320px card: the email hint, an address a visitor
   pastes into the message, the option text in the code box. */
.mn-enquiry :is(p,label,li){overflow-wrap:anywhere;}

/* --------------------------------------------------------------------
   The narrow layout. Below 520px the card stops trying to be a centred
   panel and becomes a sheet on the bottom edge: full width, square top
   corners rounded, pinned to the edge a thumb reaches. Nothing is added
   or removed - the same six controls, in the column arrangement the
   container query above decides.
   -------------------------------------------------------------------- */
@media (max-width:519px){
  .mn-enquiry{
    inline-size:100%;
    max-inline-size:100%;
    /* The full height, not 100svh less an inset. A bottom sheet that
       reaches the top edge at its maximum is the pattern behaving
       normally, and the 16px this used to hold back was the last thing
       standing between the 320 x 568 card and fitting. It is still only
       a reflow guard - the card is content-sized and comes nowhere near
       this on any phone in the ladder. */
    max-block-size:100vh;
    max-block-size:100svh;
    margin-block-end:0;
    /* 20/16/16 before. Four pixels off the top and two off the bottom,
       taken where the sheet has the least to spare. */
    padding:16px 16px calc(14px + env(safe-area-inset-bottom));
    border-radius:calc(var(--r) * 3) calc(var(--r) * 3) 0 0;
  }
  /* 40 x 40 down to 36 x 36, and the -4px lift it used to need to sit
     level with the heading goes with it. Four more pixels off the header
     row, and still half again the 24 x 24 minimum. */
  .mn-enquiry__close{
    inline-size:36px;block-size:36px;
    margin-block-start:0;
  }
  @starting-style{
    .mn-enquiry[open]{translate:0 24px;}
  }
}

@media (prefers-reduced-motion:reduce){
  .mn-enquiry,
  .mn-enquiry__submit,
  .mn-enquiry__close,
  .mn-enquiry__form :is(input,select,textarea){transition:none;}
  .mn-enquiry{translate:none;}
  .mn-enquiry:not([open]){translate:none;}
  .mn-enquiry__submit:hover{transform:none;}
}
