/* ==========================================================================
   panel.css — THE OVERRIDE LAYER.

   READ THIS BEFORE EDITING EITHER THIS FILE OR site.css.

   Load order, always: tokens.css, site.css, THIS FILE LAST. This file is
   designed to win where it overlaps site.css — that is its whole contract —
   and build.py's check_nothing_hidden_without_a_revealer() asserts the order
   from the emitted <link> tags of every page, so an accidental reorder
   breaks the build rather than the design.

   ⚠️ EVERY CUSTOM PROPERTY BELOW IS ONE tokens.css ALREADY DEFINES. An
   undefined var() does not fall back — it deletes the whole declaration —
   so a private token invented here would silently strip the rule that uses
   it. Define new tokens in tokens.css, never here.

   THE ONLY THING THIS FILE HIDES: `.rise`, and ONLY when `html.js` is
   present (added by an inline script in build.py's page template BEFORE
   this stylesheet loads, so there is no flash of the wrong state on a
   JS-capable browser). Content-visible-by-default lives in site.css; this
   file is what turns that into a scroll reveal WHEN a script exists to
   un-hide it — assets/js/main.js's `rise()` behaviour adds `.in` on
   intersection. check_nothing_hidden_without_a_revealer() fails the build
   if this file ever hides a class with no matching revealer in assets/js/.
   ========================================================================== */

html.js .rise {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--dur-med) var(--ease), transform var(--dur-med) var(--ease);
}
html.js .rise.in {
  opacity: 1;
  transform: none;
}

/* An element GSAP drives keeps the flash-free hidden state above but hands the
   MOTION to main.js. Leaving the transition on means two systems animate the
   same two properties at once; GSAP writes inline styles, which win, so the
   CSS transition only ever arrives late and fights the clearProps handback. */
html.js .rise[data-m] {
  transition: none;
}

/* prefers-reduced-motion: the reveal still happens (content is not gated
   behind scroll for a reduced-motion visitor), it just does not animate. */
@media (prefers-reduced-motion: reduce) {
  html.js .rise,
  html.js .rise.in {
    transition: none;
  }
}

/* ==========================================================================
   HEADER — the two things the migrated Theme Builder chrome cannot do itself.
   Both measured on staging 2026-09-26, both at the artefact, not in theory.
   ========================================================================== */

/* 1. THE DESKTOP DROPDOWNS RUN OFF THE RIGHT EDGE.
      The last two top-level items open a 300px panel anchored to their own left edge, so
      RESOURCES opened to right=1518 in a 1440 viewport -- 78px of the menu unreachable, and a
      horizontal scrollbar on every page. Anchoring the last two to their RIGHT edge keeps the
      panel on screen at any width without touching the other five. */
@media (min-width: 981px) {
  header .iif-menu > li:nth-last-child(-n+3) > ul.sub-menu {
    left: auto;
    right: 0;
  }
}

/* 2. NO MOBILE AFFORDANCE INSIDE THE BAR.
      Divi's own mobile nav is script-driven, so the build replaced it with a <details> that
      opens with no JavaScript (grid.css). But it is a SIBLING of the header bar in the markup,
      and left in flow it stacked BELOW the teal band as a 40px strip of white with the word
      "Menu" in it. <header> is already position:relative, so it can be anchored into the band
      with no change to the migrated markup.

      The logo was clipped at x=0 in the same pass: the header row runs edge to edge at this
      width, and the logo is sized in % of a column that is the full viewport. */
@media (max-width: 980px) {
  header .row { padding-left: 5%; padding-right: 5%; }
  header .m-image.i-0-tb_header { width: auto; }
  header .m-image img { max-width: 160px; height: auto; }

  header .mobile-nav {
    position: absolute;
    top: 0;
    right: 5%;
    height: 100%;
    display: flex;
    align-items: center;
    z-index: 20;
  }
  /* The label stays in the accessibility tree (aria-label on <summary>); only the glyph
     changes. font-size:0 collapses the text without removing it from the element. */
  header .mobile-nav summary {
    font-size: 0;
    width: 30px;
    height: 22px;
    padding: 0;
    position: relative;
  }
  header .mobile-nav summary::before,
  header .mobile-nav summary::after,
  header .mobile-nav summary span.bar {
    content: "";
    position: absolute;
    left: 0;
    width: 30px;
    height: 3px;
    border-radius: 2px;
    background: #fff;
    transition: transform var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
  }
  header .mobile-nav summary::before { top: 3px; }
  header .mobile-nav summary::after { bottom: 3px; }
  header .mobile-nav summary span.bar { top: 50%; margin-top: -1.5px; }

  header .mobile-nav[open] summary::before { transform: translateY(7.5px) rotate(45deg); }
  header .mobile-nav[open] summary::after { transform: translateY(-7.5px) rotate(-45deg); }
  header .mobile-nav[open] summary span.bar { opacity: 0; }

  /* The panel drops out of the bar over the page, full width, so a 26-item menu is reachable
     without the bar growing to hold it. */
  /* UTILITY_MAP renames Divi's `iif-mobile-menu` to `mobile-menu`, which is our <details>
     panel's class too -- so Divi's `display:none` (it expects its own script to add `.opened`)
     lands on our panel and the disclosure opened onto nothing. The whole point of <details>
     is that it needs no script, so the override goes here rather than into main.js. */
  header .mobile-nav[open] .mobile-menu { display: block; }
  header .mobile-nav .mobile-menu {
    position: absolute;
    top: 100%;
    /* Divi's base rule sets `left:0`. With left AND right both set, width wins and `right` is
       ignored, which anchored the panel to the hamburger's own left edge at x=341. */
    left: auto;
    right: -5vw;
    width: 100vw;
    max-height: 70vh;
    overflow-y: auto;
    background: var(--c-teal);
    padding: 0.5rem 0 1.5rem;
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.18);
  }
  /* The gutter lives on the LINKS, not the panel: site.css carries
     `.mobile-menu{padding-left:0px!important}` and !important beats any specificity this
     file can reach. Padding the anchors also makes the whole row tappable to the edge,
     which a padded container would not. */
  header .mobile-nav .mobile-menu a {
    color: #fff;
    padding: 0.6em 5%;
  }
  header .mobile-nav .mobile-menu ul a {
    opacity: 0.85;
    font-size: 0.95em;
    padding-left: 9%;
  }
}

/* FOOTER — "Quick Links" was the only heading in the footer that was centred, sitting over a
   two-column list that is left-aligned, while "Contact Us" beside it is left-aligned by the
   client's own rule. Matching it to its list and to its neighbour. */
.main_footer .col-1_2.col.i-1-tb_footer h2 { text-align: left; }
/* The social row sat flush against its heading while Quick Links had 35px under its own. */
.main_footer .footer_social { margin-top: 14px; }

/* BLOG LISTING — the pagination was an unstyled row of default-size links tucked under the
   last card, which read as a page that had not been finished. Centred, with real tap targets. */
.pagination {
  justify-content: center;
  align-items: center;
  gap: 0.35em;
  margin: 3.5em 0 1em;
  flex-wrap: wrap;
}
.pagination a,
.pagination .current {
  display: inline-block;
  min-width: 2.4em;
  padding: 0.55em 0.75em;
  text-align: center;
  border-radius: 4px;
  font-size: 0.95rem;
  line-height: 1;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.pagination a { color: var(--c-teal-dark); }
.pagination a:hover { background: var(--c-teal); color: #fff; }
.pagination .current { background: var(--c-teal-dark); color: #fff; font-weight: 600; }
.pagination .prev, .pagination .next { font-weight: 600; }

/* `.blog-grid-inner{display:contents}` promotes the cards into the row's flex context so they
   sit on one line -- and the pagination, a sibling, became a flex ITEM alongside them and
   rendered ON TOP of the third card. It needs its own line. */
.pagination { flex: 0 0 100%; width: 100%; }

/* BLOG GRID — the cards overflowed their own container by 172px, so the pagination (a block
   sibling of .blog-grid) was laid out 172px too high and rendered ON TOP of the third card.
   grid.css asks for `.blog-grid-inner{display:contents}` so the cards become direct children
   of the grid's flex context, but something later in the cascade computes it back to `flex`,
   leaving a nested flex item whose height did not cover its own wrapped rows.
   Measured, not assumed: .blog-grid bottom 2879 vs last card bottom 3051.
   The outer becomes a plain block (its height is then simply its content) and the inner is the
   one flex row that wraps. */
.blog-grid { display: block; }
.blog-grid-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
}


/* --- self-hosted video (replaces the dead Vimeo embed) --- */
.m-video { position: relative; }
.m-video_el {
  display: block;
  width: 100%;
  height: auto;
  background: #000;
}
.m-video .iif-video-overlay { display: block; }
.m-video.is-playing .iif-video-overlay { display: none; }
