/* ---------------------------------------------------------------------------
   Sankartik — components: header, mega-menu, footer, mobile bar
--------------------------------------------------------------------------- */

/* Header -----------------------------------------------------------------
   An opaque bar: background is var(--ground-dark) #0E2A24, with no alpha and
   no backdrop-filter, so the rendered colour is #0E2A24 at every scroll
   position regardless of what passes beneath the sticky bar.

   Its contents take the DARK ground pair on that background: var(--on-dark)
   #F7F8F6 at 14.33:1 and var(--on-dark-2) #A8B5AE at 7.19:1, both AA.
   #5A564E is 2.09:1 here and appears in no rule in this file. */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--ground-dark);
  color: var(--on-dark);
  border-bottom: var(--hairline-w) solid var(--hairline);
}

.header-inner {
  display: flex;
  align-items: center;
  gap: var(--space-l);
  min-height: var(--header-h);
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Logo. logo-header.njk holds nine outlined glyphs, all on baseline y=106,
   in viewBox "0 0 602 167.14". It contains no descriptor band, no <rect> rule
   and no ground rect.

   The Minimum sizes table in assets/README.md governs it by the "Wordmark
   alone" row: 140px screen, 34mm print. The "Wordmark with descriptor" row is
   420px / 65mm and does not apply to this cut. That table marks every figure
   provisional until the print proof returns, which still governs the print
   column.

   The widths set below are 168px, and 190px above 1200px. Both are CONFIRMED
   ON SCREEN by the on-device check of 14 September 2026: the wordmark reads as
   a word at 168px in the 72px bar. They are no longer provisional on screen. */

.logo {
  display: block;
  flex: 0 0 auto;
  line-height: 0;
}

.logo-svg {
  width: 150px;
  height: auto;
}

/* The header cut's viewBox is 602 x 167.14, a ratio of 3.602:1, so these
   widths render it 46.64px tall at 168px and 52.75px tall at 190px — both
   inside the 72px --header-h bar. */
.logo .logo-svg--header { width: 168px; }

@media (min-width: 1200px) {
  .logo .logo-svg--header { width: 190px; }
}

/* Navigation -------------------------------------------------------------- */

.nav { margin-inline: auto; }

.nav-list {
  display: flex;
  align-items: center;
  gap: var(--space-l);
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-item { margin: 0; position: static; }

.nav-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-ui);
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  color: var(--on-dark);
  text-decoration: none;
  background: none;
  border: 0;
  padding: var(--space-xs) 0;
  cursor: pointer;
  position: relative;
}

/* The active underline slides between items rather than appearing. */
.nav-link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-enter) var(--ease-out);
}
.nav-link:hover::after,
.nav-link.is-current::after,
.nav-trigger[aria-expanded="true"]::after { transform: scaleX(1); }

.nav-chev { width: 10px; height: 6px; transition: transform var(--dur-enter) var(--ease-out); }
.nav-trigger[aria-expanded="true"] .nav-chev { transform: rotate(180deg); }

/* Mega-menu ---------------------------------------------------------------
   One only, on "What We Do". Full-width panel below the nav.
   Nothing here is discoverable by hover alone: the panel opens on click or
   keyboard focus, and closes on Escape. */

.megamenu {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: var(--blur-fallback);   /* solid fallback, applied first */
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  color: var(--on-dark);
}

/* Backdrop blur is expensive on mid-range Android — a significant share of the
   audience — so the translucent treatment is opt-in behind @supports. */
@supports (backdrop-filter: blur(1px)) {
  .megamenu {
    background: rgba(11, 35, 30, 0.88);
    backdrop-filter: blur(14px);
  }
}

.megamenu[hidden] { display: none; }

.megamenu.is-open {
  animation: megamenu-in var(--dur-enter) var(--ease-out) both;
}

@keyframes megamenu-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.megamenu-inner {
  display: grid;
  grid-template-columns: minmax(220px, 300px) 1fr;
  gap: var(--space-2xl);
  padding-block: var(--space-xl);
}

/* Hairline vertical rule between the columns. */
.megamenu-list {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--space-2xl);
  border-left: 1px solid var(--hairline);
  display: grid;
  gap: var(--space-m);
}

.megamenu-intro .eyebrow { color: var(--on-dark-2); }
.megamenu-desc { color: var(--on-dark-2); font-size: var(--step--1); }

.megamenu-link {
  display: block;
  text-decoration: none;
  padding: var(--space-2xs) 0;
  transition: transform var(--dur-enter) var(--ease-out);
}
/* List items lift on hover: a small x-shift, no scaling, no colour flash. */
.megamenu-link:hover { transform: translateX(4px); }

.megamenu-link-title {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--step-0);
  color: var(--on-dark);
}

.megamenu-link:hover .megamenu-link-title { text-decoration: underline; }

/* Header actions ---------------------------------------------------------- */

.header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-m);
  flex: 0 0 auto;
}

.header-phone {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  color: var(--on-dark);
  text-decoration: none;
  white-space: nowrap;
}
.header-phone:hover { text-decoration: underline; }

.btn--sm { font-size: var(--step--1); padding: var(--space-xs) var(--space-m); }

/* Mobile toggle and navigation -------------------------------------------- */

.nav-toggle {
  display: none;
  background: none;
  border: 0;
  color: var(--on-dark);
  padding: var(--space-xs);
  cursor: pointer;
  margin-left: auto;
}
.nav-toggle-bars { display: grid; gap: 4px; width: 22px; }
.nav-toggle-bars i { display: block; height: 1.5px; background: currentColor; }

.mobile-nav {
  background: var(--blur-fallback);
  border-top: 1px solid var(--hairline);
  color: var(--on-dark);
}
.mobile-nav[hidden] { display: none; }

.mobile-nav-list {
  list-style: none;
  margin: 0;
  padding-block: var(--space-m);
  display: grid;
  gap: var(--space-2xs);
}
.mobile-nav-list a {
  display: block;
  font-family: var(--font-ui);
  padding: var(--space-s) 0;
  text-decoration: none;
  color: var(--on-dark);
}
.mobile-nav-sub a {
  padding-left: var(--space-m);
  color: var(--on-dark-2);
  font-size: var(--step--1);
}

@media (max-width: 960px) {
  .nav, .header-actions { display: none; }
  .nav-toggle { display: block; }
}

/* Footer ------------------------------------------------------------------ */

.site-footer { padding-block: var(--space-2xl) var(--space-l); }

.footer-inner {
  display: grid;
  grid-template-columns: 1.4fr repeat(3, 1fr);
  gap: var(--space-xl);
}

.footer-logo { display: block; line-height: 0; margin-bottom: var(--space-m); }
.footer-logo .logo-svg { width: 190px; }

.footer-line { font-size: var(--step--1); max-width: 32ch; }

.footer-heading {
  font-family: var(--font-ui);
  font-size: var(--step--1);
  font-weight: 400;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--on-dark-2);
  margin: 0 0 var(--space-s);
}

.footer-list {
  list-style: none;
  margin: 0 0 var(--space-m);
  padding: 0;
  display: grid;
  gap: var(--space-xs);
  font-size: var(--step--1);
}
.footer-list a { text-decoration: none; }
.footer-list a:hover { text-decoration: underline; }

.footer-address, .footer-response { font-size: var(--step--1); margin: 0; }

/* Social links — inside .footer-brand, after the footer line. Deliberately a
   ROW, not a column: it sits in the brand block rather than being a fifth
   footer column, so it must not read as another link list. It wraps instead of
   forcing width, so .footer-inner's grid and both its breakpoints are
   untouched — no grid-template-columns change, no new media query.

   Text links, not icons: assets/ ships no platform glyphs, and the avatar PNGs
   are the Sankartik monogram, which would show one mark three times. Labels are
   approved copy, content file :746 ("- The owner decided the registration"). */
.footer-social { margin-top: var(--space-m); }

.footer-social-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-m);
  font-size: var(--step--1);
}

.footer-social-list a { text-decoration: none; }
.footer-social-list a:hover { text-decoration: underline; }

.footer-legal {
  margin-top: var(--space-xl);
  padding-top: var(--space-m);
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m) var(--space-xl);
  justify-content: space-between;
}
.footer-legal p { font-size: var(--step--1); margin: 0; }

/* 900px, not 860px. At 861px the four-column 1.4fr share drops to 217px while
   the three social links need 224.2px, so LinkedIn wrapped across an 861–893px
   band — a discontinuity at the breakpoint, not a gradual squeeze. Two columns
   up to 900px clears it. Measured 25 September 2026. */
@media (max-width: 900px) {
  .footer-inner { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  .footer-inner { grid-template-columns: 1fr; }
}

/* Mobile bar --------------------------------------------------------------
   Fixed bottom. Only on small screens; the body gains padding so the bar
   never covers the end of the footer. */

.mobile-bar { display: none; }

/* No mobile bar at this width, but an open banner still covers the end of the
   document, so the last controls on a page must be able to scroll clear of it.
   Resolves to zero when the banner is closed or absent, which is every page
   today. Owner decision, 26 September 2026. */
body { padding-bottom: var(--consent-h, 0px); }

@media (max-width: 960px) {
  .mobile-bar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 120;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    background: var(--ground-dark);
    border-top: var(--hairline-w) solid var(--hairline);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }

  .mobile-bar-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: var(--mobilebar-h);
    font-family: var(--font-ui);
    font-size: var(--step--1);
    color: var(--on-dark);
    text-decoration: none;
  }

  .mobile-bar-item--cta { background: var(--ground-light); color: var(--ground-dark); }
  .mobile-bar-icon { width: 20px; height: 20px; }

  /* --mobilebar-outer-h, not --mobilebar-h: the bar's 1px top border sits
     outside its min-height, so it renders 61px and the reservation was 1px
     short at the foot of every page below 960px. The outer token already
     existed (tokens.css:145) for exactly this distinction and is used by
     scroll-padding-bottom in base.css:86. Measured 25 September 2026. */
  /* --consent-h is the open banner's MEASURED height, published by consent.js
     and removed when it closes, so this falls back to the mobile-bar value
     whenever the banner is absent, closed or JavaScript is off. ADDED to that
     value, never substituted: on a phone both bars are present and a focused
     control must clear both. Owner decision, 26 September 2026. */
  body { padding-bottom: calc(var(--mobilebar-outer-h) + var(--consent-h, 0px)); }
}

/* Mega-menu stacks on narrow screens where it is still rendered. */
@media (max-width: 1024px) {
  .megamenu-inner { grid-template-columns: 1fr; gap: var(--space-l); }
  .megamenu-list {
    padding-left: 0;
    border-left: 0;
    border-top: 1px solid var(--hairline);
    padding-top: var(--space-l);
  }
}

/* Analytics consent banner ------------------------------------------------
   Build Specification section 11, "Consent". Owner decisions, 26 September
   2026: dark ground, flush above the mobile bar on phones, --hairline seam,
   no motion.

   HIDDEN BY DEFAULT, shown only when script sets data-consent-open. This is
   what makes "no JavaScript means no banner" true in the markup rather than
   by intention: with script off the [hidden] attribute is never removed.

   z-index 130 sits ABOVE the mobile bar's 120, but the two never overlap —
   the bottom offset below keeps them flush, not stacked. The order matters
   only if a future change gives them a shared edge case.

   No motion: no transition and no transform. The spec forbids it here, and
   a banner that animates in draws the eye away from the page. */
.consent {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 130;
  border-top: var(--hairline-w) solid var(--hairline);
}
.consent[hidden] { display: none; }

.consent-inner {
  max-width: var(--container);
  margin: 0 auto;
  padding: var(--space-s) var(--gutter);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s) var(--space-m);
}

/* The sentence keeps the site reading measure; the actions never wrap
   between themselves. */
.consent-text { margin: 0; flex: 1 1 24ch; }
.consent-actions {
  margin: 0;
  display: flex;
  gap: var(--space-s);
  flex: 0 0 auto;
}

/* Phones: flush above the mobile bar, offset by the token that already
   accounts for that bar's own top border. Owner decision, 26 September 2026:
   the banner's bottom edge meets the bar's top edge, and the boundary is the
   --hairline above. Same breakpoint the mobile bar uses, so the offset applies
   exactly when the bar is shown. */
@media (max-width: 960px) {
  .consent { bottom: var(--mobilebar-outer-h); }
}

/* STACKED AT 960px AND BELOW — owner decision, 26 September 2026, closing the
   two questions this raised.
   -------------------------------------------------------------------------
   The banner is stacked EXACTLY WHEN THE MOBILE BAR SHOWS. Same breakpoint as
   the rule above and as components.css's .mobile-bar, so the two layouts and
   the bar's presence change together at one width, never at two.

   WHY THIS EXISTS. Before it, the row collapsed by wrapping whenever the text
   and the buttons stopped fitting side by side, which happened near 500px --
   not at 960px. Two measured consequences, both now gone:
     - Between about 500 and 530px the text column was squeezed to 222-250px,
       NARROWER THAN THE 221px BUTTON GROUP BESIDE IT, and the sentence ran to
       six lines. At 480px, one step narrower, the stacked layout gave the same
       sentence the full 440px and four lines: the narrower viewport read better
       than the wider one.
     - The stacked layout the specification describes for "960px and below" was
       in fact reached only below about 500px.

   NO NEW VALUE. 100% is the full width of the container the banner already
   has, not a chosen width; 0 and 1 are flex factors, not spacing. The gaps,
   padding, alignment and button treatment are the ones already declared above.

   BOTH CHILDREN take the full line, because giving it to the text alone is not
   enough: --consent-text keeps flex-shrink 1 from the base rule, so at 940-960px
   it shrank below its own basis and sat beside the buttons anyway. Measured, not
   assumed.

   PROVEN NOT TO CHANGE THE PHONE. At 390px the banner renders PIXEL-IDENTICAL
   to before this rule: same clipped region, same SHA-256, 13697 bytes. The
   buttons keep their exact positions (Accept 18-119, Decline 131-238), the 12px
   row gap and the 170px height. */
@media (max-width: 960px) {
  .consent-text { flex: 1 1 100%; }
  .consent-actions { flex: 0 0 100%; }
}

/* STACKED AT 960px AND BELOW — owner decision, 26 September 2026, closing the
   two questions this raised.
   -------------------------------------------------------------------------
   The banner is stacked EXACTLY WHEN THE MOBILE BAR SHOWS. Same breakpoint as
   the rule above and as components.css's .mobile-bar, so the two layouts and
   the bar's presence change together at one width, never at two.

   WHY THIS EXISTS. Before it, the row collapsed by wrapping whenever the text
   and the buttons stopped fitting side by side, which happened near 500px --
   not at 960px. Two measured consequences, both now gone:
     - Between about 500 and 530px the text column was squeezed to 222-250px,
       NARROWER THAN THE 221px BUTTON GROUP BESIDE IT, and the sentence ran to
       six lines. At 480px, one step narrower, the stacked layout gave the same
       sentence the full 440px and four lines: the narrower viewport read better
       than the wider one.
     - The stacked layout the specification describes for "960px and below" was
       in fact reached only below about 500px.

   NO NEW VALUE. 100% is the full width of the container the banner already
   has, not a chosen width; 0 and 1 are flex factors, not spacing. The gaps,
   padding, alignment and button treatment are the ones already declared above.

   BOTH CHILDREN take the full line, because giving it to the text alone is not
   enough: --consent-text keeps flex-shrink 1 from the base rule, so at 940-960px
   it shrank below its own basis and sat beside the buttons anyway. Measured, not
   assumed.

   PROVEN NOT TO CHANGE THE PHONE. At 390px the banner renders PIXEL-IDENTICAL
   to before this rule: same clipped region, same SHA-256, 13697 bytes. The
   buttons keep their exact positions (Accept 18-119, Decline 131-238), the 12px
   row gap and the 170px height. */
@media (max-width: 960px) {
  .consent-text { flex: 1 1 100%; }
  .consent-actions { flex: 0 0 100%; }
}
