/*
Theme Name: in2ition
Theme URI:
Author: in2ition
Description: Block theme for the in2ition redesign. Built against the in2ition design system.
Requires at least: 6.5
Tested up to: 6.8
Requires PHP: 8.0
Version: 0.1.0
License: GPL-2.0-or-later
Text Domain: in2ition
*/

/* ---------------------------------------------------------------------------
   The design system's own tokens. theme.json presets and these custom properties
   are two views of the same values; block markup uses the presets, hand-written
   CSS below uses the tokens.

   NOTE — the handoff's style.css also imports "tokens/effects.css". No such file
   exists in the design system (its tokens/ holds base, colors, figma-variables,
   fonts, motion, responsive, spacing, typography). The import is dropped rather
   than inventing a file, and the shadow/blend values it would have carried are
   documented in readme.md as "no drop shadows anywhere".
   --------------------------------------------------------------------------- */
@import url("assets/css/tokens/colors.css");
@import url("assets/css/tokens/typography.css");
@import url("assets/css/tokens/spacing.css");
@import url("assets/css/tokens/motion.css");
@import url("assets/css/tokens/responsive.css");
@import url("assets/css/tokens/base.css");

/* Fonts are declared in theme.json fontFace, not here — do NOT import the design
   system's tokens/fonts.css into the theme, or the faces will be declared twice. */

/* ---------------------------------------------------------------------------
   Value prop row — hairline dividers between columns, gone when stacked.
   --------------------------------------------------------------------------- */
.in2-value-props > .wp-block-column + .wp-block-column {
  border-left: 0.5px solid var(--border-hairline);
  padding-left: var(--in2-space-40);
}
@media (max-width: 900px) {
  .in2-value-props > .wp-block-column + .wp-block-column { border-left: 0; padding-left: 0 }
}

/* ---------------------------------------------------------------------------
   Gravity Forms, styled to brand. Targets Gravity Forms' own class names so the
   plugin keeps ownership of the markup, validation and submission.
   Disable the plugin's own CSS (Forms > Settings > Output Default CSS: off) or
   these rules will fight it.
   --------------------------------------------------------------------------- */
.in2-gform .gform_wrapper .gform_fields { display: grid; gap: var(--in2-space-25) }
.in2-gform .gform_wrapper .gfield_label {
  font-family: var(--in2-font-sans);
  font-size: var(--in2-size-small);
  font-weight: var(--in2-weight-medium);
  line-height: 1.3;
  color: var(--text-body);
  margin-bottom: var(--in2-space-10);
}
.in2-gform .gform_wrapper input[type="text"],
.in2-gform .gform_wrapper input[type="email"],
.in2-gform .gform_wrapper input[type="tel"],
.in2-gform .gform_wrapper select,
.in2-gform .gform_wrapper textarea {
  box-sizing: border-box;
  width: 100%;
  font-family: var(--in2-font-sans);
  font-size: var(--in2-size-body);
  line-height: 1.4;
  color: var(--text-body);
  background: var(--in2-white);
  border: 1px solid var(--in2-slate);
  border-radius: var(--in2-radius-none);
  padding: 0 var(--in2-space-20);
  height: 60px;
}
.in2-gform .gform_wrapper textarea { height: auto; padding: 16px var(--in2-space-20); resize: vertical }
.in2-gform .gform_wrapper input:focus,
.in2-gform .gform_wrapper select:focus,
.in2-gform .gform_wrapper textarea:focus {
  outline: none;
  border-color: var(--in2-sky);
}
/* GF emits the required marker with no separating space, so labels read "Name(Required)". */
.in2-gform .gform_wrapper .gfield_required { color: var(--in2-red); margin-left: 0.35em }
.in2-gform .gform_wrapper .gfield_description.validation_message,
.in2-gform .gform_wrapper .gform_validation_errors {
  font-size: var(--in2-size-small);
  color: var(--in2-red);
  background: none;
  border: 0;
  padding: 0;
}

/* Submit reuses the pill button. Same geometry as the Button component:
   60px tall, 2px red border, uppercase Poppins Medium, fills red on hover.

   BOTH selectors are needed. The handoff's style.css targets only
   `input[type="submit"]`, which was correct for older Gravity Forms; 3.x renders
   `<button type="submit" class="gform_button">` instead, so on this install the rule
   matched nothing and the submit rendered as a 21px grey Arial browser default while
   every other field looked right. */
.in2-gform .gform_wrapper .gform_footer input[type="submit"],
.in2-gform .gform_wrapper .gform_footer button[type="submit"],
.in2-gform .gform_wrapper .gform_footer .gform_button {
  height: 60px;
  padding: 0 30px;
  font-family: var(--in2-font-sans);
  font-size: var(--in2-size-button);
  font-weight: var(--in2-weight-medium);
  letter-spacing: var(--in2-tracking-tight);
  text-transform: uppercase;
  color: var(--in2-red);
  background: var(--surface-button);
  border: 2px solid var(--border-button);
  border-radius: var(--in2-radius-pill);
  cursor: pointer;
  transition: background 220ms ease-out, color 220ms ease-out;
}
.in2-gform .gform_wrapper .gform_footer input[type="submit"]:hover,
.in2-gform .gform_wrapper .gform_footer button[type="submit"]:hover,
.in2-gform .gform_wrapper .gform_footer .gform_button:hover {
  background: var(--in2-red);
  color: var(--in2-white);
}
.in2-gform .gform_wrapper .gform_confirmation_message {
  font-size: var(--in2-size-lead);
  line-height: 1.4;
  color: var(--text-body);
}

/* Fallback shown when Gravity Forms is not active — never a hand-written form. */
.in2-gform .gform_wrapper .gform_footer { margin-top: var(--in2-space-30); }

.in2-gform-missing {
  font-size: var(--in2-size-body);
  line-height: 1.4;
  color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   Motion. The Figma file annotates fade-in-and-up on most content blocks.
   Add .in2-reveal to a block; a small IntersectionObserver adds .is-in.
   --------------------------------------------------------------------------- */
/* The hidden state is scoped to .in2-js, which reveal.js puts on <html> only once it
   knows it can observe. Unscoped, `opacity: 0` is a content-invisibility bug waiting to
   happen: a JS error, a blocked script or an old browser would hide the page for good.
   Opt-in means the worst case is "no animation", not "no content". */
.in2-js .in2-reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 700ms ease-out, transform 700ms ease-out;
}
.in2-js .in2-reveal.is-in { opacity: 1; transform: none }
@media (prefers-reduced-motion: reduce) {
  .in2-js .in2-reveal { opacity: 1; transform: none; transition: none }
}

/* ===========================================================================
   COMPONENTS
   The design system expresses these as React inline styles. Block markup cannot
   carry them, so each component becomes a class here and the pattern/template
   carries only semantic blocks. Every value is transcribed from the component's
   .jsx — where the source says 133.5px or 314.117px, that is what appears here.

   Artwork lives in CSS rather than in block attributes because several plates need
   mix-blend-mode or precise sizing that cover/image blocks cannot express. Paths are
   relative to this file, so they resolve without get_theme_file_uri().
   =========================================================================== */

/* ---------------------------------------------------------------------------
   Header — NavBar (figma 170:371) + MobileNav (222:479)
   Desktop is exactly 133.5px and opaque; heroes begin below it, never behind.
   --------------------------------------------------------------------------- */
/* CLIENT OVERRIDE 2026-08-18: the design system says "the desktop header is exactly
   133.5px and opaque — heroes begin below it" (readme.md, Visual foundations). Joe asked
   for the opposite: hero artwork flush to the top of the page with the nav floating over
   it. The header is therefore taken out of flow. This is a deliberate departure from the
   spec, not a misreading of it.

   Because the header no longer occupies layout space, anything that would otherwise sit
   under it has to reserve the room itself — see --in2-nav-current below. */
/* WordPress puts an automatic 24px block gap between every .wp-site-blocks child. That
   shows up twice: above <main> as a strip of bare page over the hero artwork, and again
   between <main> and the footer as a band of page ground just above the CTA. Both are
   template parts / main, none of which want the gap. */
.wp-site-blocks > main,
.wp-site-blocks > .wp-block-template-part { margin-block-start: 0; }

/* CLIENT OVERRIDE 2026-08-19: the nav follows the scroll. readme.md says "Nothing is
   fixed or sticky; the page scrolls as one piece."

   `fixed`, not `sticky`: the bar is already out of flow so the hero artwork can run to the
   top of the page with the nav floating over it. `position: sticky` only works on an
   in-flow element, so using it would mean undoing that. This is a one-property change —
   nothing reserves space from the header, heroes already pad themselves by
   --in2-nav-current. */
.in2-header-wrap {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
}

/* The WordPress admin bar is itself fixed at the top, so an editor logged in would
   otherwise find the site nav hidden underneath it. Below 600px core stops fixing the
   admin bar and lets it scroll away, so the offset goes back to zero there. */
body.admin-bar .in2-header-wrap { top: 32px; }
@media (max-width: 782px) { body.admin-bar .in2-header-wrap { top: 46px; } }
@media (max-width: 600px) { body.admin-bar .in2-header-wrap { top: 0; } }

/* Scrolled state. nav.js adds .is-stuck once the page has moved past the hero's top
   band; the bar shrinks and takes a solid ground so content cannot read through it. */
.in2-header-wrap.is-stuck .in2-header {
  height: clamp(60px, 5.4vw, 78px);
  background: var(--in2-white);
  box-shadow: 0 1px 0 rgba(56, 74, 82, 0.10);
}
.in2-header-wrap.is-stuck .in2-logo img { width: clamp(104px, 8.4vw, 121px); }
.in2-header .in2-logo img,
.in2-header {
  transition: height var(--in2-duration-morph) var(--in2-ease),
              width var(--in2-duration-morph) var(--in2-ease),
              background var(--in2-duration-morph) var(--in2-ease);
}
@media (prefers-reduced-motion: reduce) {
  .in2-header, .in2-header .in2-logo img { transition: none; }
}
.in2-header {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--in2-space-40);
  height: clamp(88px, 9.3vw, 133.5px);
  padding: 0 var(--in2-fluid-gutter);
  background: transparent;
}
/* Desktop header is WHITE and opaque — back to what readme.md specifies ("the desktop
   header is exactly 133.5px and opaque"), per the designer's note, 2026-08-19.
   The bar still floats over the hero rather than sitting in flow, so the artwork runs to
   the top of the page and the top 133.5px of it is simply covered. Mobile keeps the
   transparent bar, where the artwork reading through a 68px strip works well.
   To revert: delete this block. To instead give the hero its full band back, the header
   would need to return to the flow — that is a bigger change, see CLAUDE.md. */
@media (min-width: 900px) {
  .in2-header { background: var(--in2-white); }
}
/* The one nav height that is actually in effect, so callers do not have to re-derive
   which breakpoint they are in every time they need to clear the bar. */
:root { --in2-nav-current: clamp(88px, 9.3vw, 133.5px); }
@media (max-width: 899px) {
  :root { --in2-nav-current: var(--in2-nav-height-mobile); }
  /* Restored. This rule was lost when the nav was hand-rolled, so the bar rendered at the
     clamp's 88px floor while --in2-nav-current claimed 68px — everything that reserved
     room under the header was 20px short. The token is now the same expression as the
     height above, so the two cannot drift again. */
  .in2-header { height: var(--in2-nav-height-mobile); }
}

/* A page whose first block is not a hero still has to clear the floating nav — but the
   clearance CANNOT be expressed as "main's first child that isn't a hero". wp:post-content
   renders its own <div class="wp-block-post-content"> wrapper, so main's first child is
   always that div and never the hero itself; the rule fired on every page and pushed the
   hero artwork down behind a band of empty page. Hero-less templates opt in explicitly
   instead. */
.in2-below-nav { padding-top: calc(var(--in2-nav-current) + 40px); }
.in2-header .wp-block-site-logo,
.in2-header .in2-logo { line-height: 0; }
.in2-header .in2-logo img,
.in2-header .wp-block-site-logo img {
  width: clamp(120px, 11.3vw, 163px);
  height: auto;
  display: block;
}
.in2-nav { margin-left: auto; display: flex; gap: clamp(18px, 2.8vw, 40px); }
.in2-nav a {
  position: relative;
  font-family: var(--in2-font-sans);
  font-weight: var(--in2-weight-semibold);
  font-size: clamp(13px, 1.11vw, 16px);
  line-height: 100%;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: var(--in2-slate);
  transition: color var(--in2-duration-hover) var(--in2-ease);
}
/* "Contact Us" is always red — the one item whose colour does change. */
.in2-nav a.is-accent { color: var(--in2-red); }

/* CLIENT OVERRIDE 2026-08-18: NavBar.jsx turns a link red on hover AND when active
   (`it.accent || isActive || lit ? red : slate`), with the 2px underline reserved for the
   active item. Joe asked for the opposite emphasis — the label keeps its slate, and the
   red underline marks both hover and the current page. Contact Us stays red throughout.

   One rule drives both states so they can never drift apart. The underline is always in
   the DOM and fades, rather than appearing on hover, so it animates rather than snapping
   — the Figma note is explicit that "all hover effects happen via animation". Geometry is
   still the drawn one: 2px, 26.6px from the top of the 16px line box. */
.in2-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  top: 26.6px;
  width: 100%;
  height: 2px;
  background: var(--in2-red);
  opacity: 0;
  transition: opacity var(--in2-duration-hover) var(--in2-ease);
}
.in2-nav a:hover::after,
.in2-nav a:focus-visible::after,
.in2-nav a[aria-current="page"]::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .in2-nav a::after { transition: none; }
}

/* MobileNav: the burger is two 22x2 rounded bars that morph into an X. */
.in2-burger {
  display: none;
  position: relative;
  width: 22px;
  height: 24px;
  margin-left: auto;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}
.in2-burger span {
  position: absolute;
  left: 0;
  width: 22px;
  height: 2px;
  border-radius: 1px;
  background: var(--in2-slate);
  transition: transform var(--in2-duration-morph) var(--in2-ease),
              opacity var(--in2-duration-morph) var(--in2-ease),
              top var(--in2-duration-morph) var(--in2-ease);
}
.in2-burger span:nth-child(1) { top: 5px; }
.in2-burger span:nth-child(2) { top: 11px; }
.in2-burger span:nth-child(3) { top: 17px; }
.in2-burger[aria-expanded="true"] span:nth-child(1) { top: 11px; transform: rotate(45deg); }
.in2-burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.in2-burger[aria-expanded="true"] span:nth-child(3) { top: 11px; transform: rotate(-45deg); }

/* The open panel is solid brand red with white uppercase links, 40px apart. */
.in2-mobile-panel { display: none; background: var(--in2-red); padding: 30px var(--in2-gutter-mobile) 40px; }
.in2-mobile-panel.is-open { display: flex; flex-direction: column; gap: var(--in2-space-40); }
.in2-mobile-panel a {
  font-family: var(--in2-font-sans);
  font-weight: var(--in2-weight-semibold);
  font-size: var(--in2-size-nav);
  line-height: 100%;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--in2-white);
}
.in2-mobile-panel a:hover { color: var(--in2-white); opacity: 0.75; }
@media (max-width: 899px) {
  .in2-nav { display: none; }
  .in2-burger { display: block; }
}
@media (min-width: 900px) {
  .in2-mobile-panel { display: none !important; }
}

/* ---------------------------------------------------------------------------
   Button — the pill from the `btn` set (figma 12:4)
   theme.json carries the geometry. This adds the 30px arrow bubble for the primary
   variant and the hover fill, both of which theme.json cannot express.
   --------------------------------------------------------------------------- */
.wp-block-button .wp-block-button__link {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: var(--in2-space-10);
  height: 60px;
  line-height: 100%;
  transition: background var(--in2-duration-hover) var(--in2-ease),
              color var(--in2-duration-hover) var(--in2-ease);
}
.wp-block-button.is-style-in2-primary .wp-block-button__link { padding: 15px 25px 15px 15px; }
.wp-block-button.is-style-in2-primary .wp-block-button__link::before {
  content: "";
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: var(--in2-radius-pill);
  background: var(--in2-red);
  /* the 6px diagonal arrow, transcribed from Button.jsx */
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path d="M1.4 6.6 L6.6 1.4 M2.4 1.4 H6.6 V5.6" fill="none" stroke="white" stroke-width="1.1" stroke-linecap="square"/></svg>');
  background-repeat: no-repeat;
  background-position: center;
  transition: background-color var(--in2-duration-hover) var(--in2-ease);
}
.wp-block-button.is-style-in2-primary .wp-block-button__link:hover::before {
  background-color: var(--in2-white);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path d="M1.4 6.6 L6.6 1.4 M2.4 1.4 H6.6 V5.6" fill="none" stroke="%23AD2624" stroke-width="1.1" stroke-linecap="square"/></svg>');
}
/* On the dark approach panel the pill is white-on-transparent and inverts to red. */
.in2-on-dark .wp-block-button .wp-block-button__link {
  background: transparent;
  color: var(--in2-white);
  border-color: var(--in2-white);
}
.in2-on-dark .wp-block-button .wp-block-button__link:hover {
  background: var(--in2-white);
  color: var(--in2-red);
}

/* ---------------------------------------------------------------------------
   SectionHeading / AccentBlock
   The 100x3 rule at 80% opacity under every colored subhead is the signature.
   --------------------------------------------------------------------------- */
/* The rule sits between the heading and the copy, so it hangs off the heading rather
   than the block — that keeps it in flow order and inheriting the heading's color,
   which is what makes `currentColor` resolve to the right palette value. */
.in2-accent-heading { margin: 0 0 var(--in2-space-25); }
.in2-accent-heading::after {
  content: "";
  display: block;
  width: 100px;
  height: 3px;
  opacity: 0.8;
  background: currentColor;
  margin-top: var(--in2-space-25);
}
.in2-accent-body { color: var(--text-body); }
.in2-accent-body ul { margin: 0; }

/* ---------------------------------------------------------------------------
   PageHero — the interior band (1440x328), one flattened export per page.
   The band sits below the 133.5px nav, not behind it, so the export is band-only.
   Title sits 72px off the bottom at 1440.
   --------------------------------------------------------------------------- */
:root {
  /* Interior hero band height at 1440. The design draws 328px; shrunk 25% on Joe's
     call 2026-08-19. Both the aspect-ratio and the min-height below derive from it. */
  --in2-hero-h: 246;
}
.in2-page-hero {
  position: relative;
  /* Artwork runs to the very top of the page and the nav floats over it. The title is
     bottom-aligned, so it never collides with the bar. */
  display: flex;
  align-items: flex-end;
  /* width MUST be pinned. With only `aspect-ratio` + `min-height`, the width is not
     definite, so the browser derives it FROM the ratio: at 402px the 160px min-height
     resolved to a 702px-wide band and every interior page scrolled sideways by 300px. */
  width: 100%;
  aspect-ratio: 1440 / var(--in2-hero-h);
  min-height: clamp(120px, 16.5vw, 246px);
  overflow: hidden;
  background: var(--surface-hero) center / cover no-repeat;
  padding: 0 var(--in2-fluid-gutter) clamp(28px, 5vw, 72px);
  box-sizing: border-box;
  margin: 0;
}
.in2-page-hero > * { position: relative; max-width: var(--in2-content-width); width: 100%; margin: 0 auto; }
.in2-page-hero h1 {
  /* margin-BLOCK, not margin. `margin: 0` here also zeroed the `auto` inline margins set
     by `.in2-page-hero > *` above, which are what centre the 1170px column. Below 1440
     the gutter absorbs the difference and nothing looks wrong; above it the title stayed
     pinned to the gutter while the body copy centred — 240px apart at 1920, 560px at
     2560. This selector out-specifies the one above, so reordering would not have helped. */
  margin-block: 0;
  font-size: var(--in2-fluid-page-title);
  line-height: var(--in2-lh-page-title);
  font-weight: var(--in2-weight-medium);
  letter-spacing: var(--in2-tracking-tight);
  color: var(--text-heading);
}
.in2-hero-services   { background-image: url("assets/img/bg-hero-services.webp"); }
.in2-hero-team       { background-image: url("assets/img/bg-hero-team.webp"); }
.in2-hero-approaches { background-image: url("assets/img/bg-hero-approaches.webp"); }
.in2-hero-expertise  { background-image: url("assets/img/bg-hero-expertise.webp"); }
.in2-hero-why-us     { background-image: url("assets/img/bg-hero-why-us.webp"); }

/* ---------------------------------------------------------------------------
   Homepage — the locked composition (ui_kits/website/Home.locked.jsx.txt)
   --------------------------------------------------------------------------- */
.in2-home-hero {
  position: relative;
  overflow: hidden;
  background: url("assets/img/bg-hero.webp") center top / cover no-repeat, rgb(248, 247, 237);
  /* max(): the designed top padding, but never less than the floating nav plus air.
     At 1440 the design value wins (211px); at 402 the clamp floor is 64px, which would
     put the h1 under the 68px mobile bar, so the nav term wins instead. */
  padding: max(clamp(64px, 14.6vw, 211px), calc(var(--in2-nav-current) + 24px))
           var(--in2-fluid-gutter)
           clamp(88px, 28.4vw, 409px);
}
/* One constrained column, left-aligned inside it.
   Previously each child carried `margin-inline: auto` AND its own max-width, so an
   814px h1 centred itself inside the 1170px column — a 178px indent — and the 585px
   paragraph indented by a different 292px. Nothing lined up with each other or with
   the body copy below. The column is now the only centred thing. */
.in2-hero-inner {
  max-width: var(--in2-content-width);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(32px, 3.5vw, 50px);
}
.in2-home-hero h1 { max-width: 814px; text-transform: capitalize; margin: 0; }
.in2-home-hero p { max-width: 585px; margin: 0; }

/* What We Do rides up over the hero artwork on wide screens (responsive.css owns
   the overlap amounts; the plate gradient is from the locked file). */
/* The What We Do wrapper is alignfull, and core's alignfull rule sets explicit margins —
   which beats `.in2-container`'s `margin: 0 auto`. Combined with `.in2-container-wide`'s
   1440 cap that left the block capped but LEFT-ALIGNED, so above 1440 the plate drifted
   left of every other section (x=183 against 375 at 1920).

   Same shape as .in2-section instead: the wrapper runs full width and carries the gutters,
   and the plate is the thing that is capped and centred. */
.in2-plate-overlap { max-width: none; }
.in2-plate {
  max-width: var(--in2-content-width);
  margin-inline: auto;
  box-sizing: border-box;
  position: relative;
  z-index: 2;
  padding: clamp(32px, 4.5vw, 64px) clamp(24px, 3.4vw, 48px);
  /* ~10% more transparent than the locked file's values so the plate melds into the
     white page ground and lets the hero artwork read through it. */
  background: linear-gradient(180deg, rgba(248, 246, 242, 0.8) 10.1%, rgba(255, 255, 255, 0.9) 56.73%);
}

.in2-leadership {
  position: relative;
  overflow: hidden;
  margin-top: clamp(48px, 9.9vw, 142px);
  /* `cover` cropped the plate: the box runs ~436px tall at 1440 while the artwork is
     2200x700 (ratio 3.143 = 458px at that width), so the top and bottom of the arrows
     were being cut. `100% auto` shows the full width of the plate un-zoomed, and the
     min-height gives it exactly the room the artwork needs (100/3.143 = 31.8vw). */
  min-height: 31.8vw;
  background: url("assets/img/bg-leadership.webp") center / 100% auto no-repeat, var(--in2-cream);
}
/* The 557px cap belongs to the COPY, not to the section. It used to sit on
   `.in2-leadership > *`, which is the padded .in2-section — so the whole band, gutters
   included, was squeezed to 557px and the text wrapped in a ~287px ribbon. */
/* Constrain the CHILDREN, not the block. Two reasons:
   1. `.in2-section > *` sets max-width:1170 with identical specificity and appears later
      in this file, so a bare `.in2-narrow { max-width: 557px }` silently lost and the
      copy ran the full 1170.
   2. The 1170 column is what gets centred. If .in2-narrow were itself capped at 557 and
      left-aligned, it would hug the padding edge above 1440px and stop lining up with
      every other section. Keeping it as the column and capping its contents means the
      copy stays put at any viewport. */
.in2-narrow > * { max-width: 557px; }

/* The continuous field: Our Services -> wave -> Why in2ition is ONE gradient, not
   three sections with backgrounds. Stops are verbatim from the locked homepage. */
.in2-field {
  background: linear-gradient(180deg,
    var(--surface-page) 0%,
    var(--in2-sand-ground) 42%,
    var(--in2-sand-ground) 64%,
    var(--surface-page) 84%);
}
/* Tighter than the standard 96px section rhythm on the two edges that meet the wave, so
   the ribbon sits close to the copy either side of it rather than floating in dead space. */
.in2-field > .in2-section:first-child { padding-bottom: clamp(20px, 2vw, 30px); }
.in2-field > .in2-why { padding-top: clamp(16px, 1.6vw, 24px); }

/* wave-ink is keyed off its plate ground so it floats on the gradient above rather
   than carrying an opaque band of its own. */
/* The locked homepage draws this at 1440/497. Trimming is not an option — the plate is
   solid ink edge to edge, only 3.4% of its height is transparent padding — so the band
   is shortened by scaling rather than cropping, which keeps the full sweep of the wave
   instead of cutting its crests. Tune --in2-wave-height alone; `100% 100%` sizing means
   the artwork follows it. */
:root { --in2-wave-height: 325; }
.in2-wave {
  width: 100%;
  aspect-ratio: 1440 / var(--in2-wave-height);
  background: url("assets/img/wave-ink.webp") center / 100% 100% no-repeat;
}

/* The colored approach panel: flattened export carrying the rotated gradient plus
   the two 50%-opacity multiply tint circles. */
.in2-approach {
  position: relative;
  overflow: hidden;
  /* Same trap as .in2-page-hero: pin the width so min-height cannot inflate it. */
  width: 100%;
  max-width: 100%;
  min-height: 560px;
  aspect-ratio: 585 / 722;
  background: url("assets/img/bg-approach.webp") center / cover no-repeat;
  padding: clamp(40px, 4.8vw, 69px) clamp(28px, 3.3vw, 47px);
  box-sizing: border-box;
  color: var(--in2-white);
}
/* The locked homepage lays this panel out as a flex column with a 28px gap; without it
   the heading butts straight into the first bullet. */
.in2-approach {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 28px;
}
.in2-approach > * { margin-block: 0; }
.in2-approach h2, .in2-approach h3, .in2-approach li, .in2-approach p { color: var(--in2-white); }
.in2-approach ul { margin: 0; padding-left: 20px; font-weight: var(--in2-weight-medium); }
.in2-approach li + li { margin-top: var(--in2-space-10); }

/* ---------------------------------------------------------------------------
   ValueProp — icon, colored title, body. Icons keep their designed color; never
   recolor an icon to match its title.
   --------------------------------------------------------------------------- */
/* ValueProp.jsx: a flex column, align-items flex-start, gap var(--in2-space-40).
   It used to get its spacing from the browser's default h3/p margins, which worked while
   this was raw pattern HTML. As real blocks those margins are zeroed — by core's own
   layout CSS and by our block-gap reset — so the card needs to own its spacing. */
.in2-valueprop {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--in2-space-40);
}
.in2-valueprop img { height: 52px; width: auto; display: block; }
.in2-valueprop p {
  line-height: 1.3;
  letter-spacing: var(--in2-tracking-tight);
  color: var(--text-muted);
}
/* the nine-up Why Us grid uses 24px titles, the homepage row 32px */
.in2-why-grid { display: grid; grid-template-columns: repeat(3, 1fr); column-gap: 40px; row-gap: 70px; }
@media (max-width: 899px) { .in2-why-grid { grid-template-columns: 1fr; row-gap: 48px; } }

/* ---------------------------------------------------------------------------
   InfoPanel — flat cream rectangle, square corners, no border, no shadow.
   --------------------------------------------------------------------------- */
.in2-panel {
  box-sizing: border-box;
  background: var(--surface-panel);
  padding: var(--in2-panel-padding);
}

/* ---------------------------------------------------------------------------
   ProcessBand — five interlocking chevrons (figma 194:936)
   314.117 x 99.842 each, separated by a 5.702px cream stroke. Below 900px each
   becomes a full-width 44px bar with its deliverables beneath (216:1002).

   ONE copy of the content serves both layouts. It used to emit each stage three times
   — the chevron row, the list row, then both again for mobile — which was fine inside a
   PHP loop but is a trap the moment the content is editable: three copies of the same
   words for a human to keep in sync, paired positionally.

   `display: contents` is what makes one markup do both. On desktop each stage wrapper
   dissolves, so its bar and list become direct grid items: the five bars land in row 1
   and the five lists in row 2, exactly the drawn layout. Below the breakpoint the wrapper
   becomes a normal block and the grid drops to one column, so each stage is a bar with
   its own list underneath.
   --------------------------------------------------------------------------- */
/* Stacked is the DEFAULT; the five-across row is the enhancement.

   The row needs real width: five labels plus their padding want ~1400px, so at the old
   900px breakpoint each track was 180px with 108px of text room and three labels clipped.
   It now turns on at 1100px, and the label size and padding scale with the viewport so the
   longest label ("Commercialization") fits at every width in between. */
.in2-process {
  display: grid;
  grid-template-columns: 1fr;
  row-gap: 18px;
}
.in2-process__stage { display: block; }
.in2-process__bar {
  box-sizing: border-box;
  height: 44px;
  width: 100%;
  padding: 0 30px 0 16px;
  margin: 0;
  display: flex;
  align-items: center;
  color: var(--in2-white);
  font-family: var(--in2-font-sans);
  font-weight: var(--in2-weight-medium);
  font-size: 16px;
  line-height: 1.2;
  clip-path: polygon(0 0, calc(100% - 14.158px) 0, 100% 50%, calc(100% - 14.158px) 100%, 0 100%);
}
.in2-process ul {
  margin: 14px 0 0;
  padding-left: 20px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-body);
}

@media (min-width: 1100px) {
  .in2-process {
    /* minmax(0, 1fr), not 1fr. A bare 1fr is minmax(auto, 1fr), so a long label pushes its
       own track wider than the rest — the five chevrons came out uneven. */
    grid-template-columns: repeat(5, minmax(0, 1fr));
    row-gap: var(--in2-space-25);
    /* Zero: the 5.702px cream separation is cut by the clip-path and the overhang below,
       not by a track gap. The group block's own blockGap would otherwise steal ~35px a bar. */
    column-gap: 0;
  }
  /* `display: contents` preserves DOM order, so the flattened items run bar1, list1, bar2 …
     Auto-placement would pack them row-major and split the bars across two rows; pinning
     the row per kind and the column per stage puts all five bars in row 1. */
  .in2-process__stage { display: contents; }
  .in2-process__bar { grid-row: 1; }
  .in2-process ul   { grid-row: 2; }
  .in2-process__stage:nth-child(1) > * { grid-column: 1; }
  .in2-process__stage:nth-child(2) > * { grid-column: 2; }
  .in2-process__stage:nth-child(3) > * { grid-column: 3; }
  .in2-process__stage:nth-child(4) > * { grid-column: 4; }
  .in2-process__stage:nth-child(5) > * { grid-column: 5; }

  .in2-process__bar {
    height: 99.842px;
    /* Overhang into the next track so the chevrons interlock. A width, not a negative
       margin: a negative margin on a grid item feeds back into track sizing and made the
       columns uneven. */
    width: calc(100% + 14.298px);
    padding: 0 clamp(14px, 1.9vw, 28px) 0 clamp(20px, 3vw, 44px);
    font-size: clamp(15px, 1.6vw, 24px);
    clip-path: polygon(0 0, calc(100% - 24px) 0, 100% 50%, calc(100% - 24px) 100%, 0 100%, 24px 50%);
  }
  .in2-process__stage:first-child .in2-process__bar {
    padding-left: clamp(14px, 1.9vw, 28px);
    clip-path: polygon(0 0, calc(100% - 24px) 0, 100% 50%, calc(100% - 24px) 100%, 0 100%);
  }
  .in2-process__stage:last-child .in2-process__bar { width: 100%; }

  .in2-process ul {
    margin: 0;
    font-size: var(--in2-size-body);
    line-height: var(--in2-lh-body);
  }
}

/* stage colors run blue, sky, purple, green, teal, left to right, always */
.in2-stage-1 { background: var(--in2-blue); }
.in2-stage-2 { background: var(--in2-sky); }
.in2-stage-3 { background: var(--in2-purple); }
.in2-stage-4 { background: var(--in2-green); }
.in2-stage-5 { background: var(--in2-teal); }

/* ---------------------------------------------------------------------------
   Team — TeamCard (figma 171:222) and the TeamBio overlay (194:1029)
   340x461 cream panel, square 340x340 photo flush to the top corners, name plate
   34px in from the left. Photos scale ~4% on hover.
   --------------------------------------------------------------------------- */
.in2-team-grid { display: flex; flex-wrap: wrap; gap: 40px; }
.in2-team-card { width: 340px; max-width: 100%; background: var(--surface-panel); }
.in2-team-card__trigger {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.in2-team-card__photo { display: block; width: 100%; aspect-ratio: 1 / 1; overflow: hidden; }
.in2-team-card__img,
.in2-team-card__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--in2-duration-morph) var(--in2-ease);
}
.in2-team-card__img--empty { background: var(--in2-beige-deep); }
.in2-team-card--has-bio:hover .in2-team-card__img,
.in2-team-card--has-bio:focus-within .in2-team-card__img {
  transform: scale(var(--in2-photo-hover-scale));
}
.in2-team-card__plate { display: flex; flex-direction: column; padding: 28px 34px 34px; }
.in2-team-card__name {
  font-weight: var(--in2-weight-medium);
  font-size: var(--in2-size-name);
  line-height: 1.2;
  color: var(--text-heading);
}
.in2-team-card__role {
  font-weight: var(--in2-weight-regular);
  font-size: var(--in2-size-body);
  line-height: 1.2;
  color: var(--text-heading);
}

/* The overlay is a native <dialog>: focus trapping, Escape and inertness come free. */
.in2-team-bio {
  width: min(1170px, calc(100vw - 48px));
  max-height: 84vh;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: visible;
}
.in2-team-bio::backdrop { background: rgba(56, 74, 82, 0.45); }
.in2-team-bio__inner {
  position: relative;
  box-sizing: border-box;
  background: var(--in2-white);
  padding: 60px 80px;
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: var(--in2-space-50);
  max-height: 84vh;
  overflow: auto;
}
.in2-team-bio__body { display: flex; flex-direction: column; gap: var(--in2-space-25); }
.in2-team-bio__name { font-weight: var(--in2-weight-medium); font-size: 40px; line-height: 1.2; color: var(--text-heading); }
.in2-team-bio__role { font-weight: var(--in2-weight-medium); font-size: var(--in2-size-name); line-height: 1.4; color: var(--text-heading); }
.in2-team-bio__copy { font-size: var(--in2-size-body); line-height: var(--in2-lh-body); color: var(--text-body); }
/* 1.4em, NOT var(--in2-lh-body). That token is the unitless line-height `1.4`, which is
   invalid as a length, so the declaration was dropped and every paragraph ran into the
   next one. TeamBio.jsx has the same bug — it feeds the token to a flex `gap` — which is
   why it was carried over here. 1.4em is what both were reaching for: one line of body. */
.in2-team-bio__copy p { margin: 0 0 1.4em; }
.in2-team-bio__copy p:last-child { margin-bottom: 0; }
.in2-team-bio__aside { display: flex; flex-direction: column; gap: var(--in2-space-20); }
/* Direct child only — the portrait. The LinkedIn glyph is an <img> inside the <a>, so an
   unscoped `.in2-team-bio__aside img` blew it up to the full 340px portrait width. */
.in2-team-bio__aside > img { width: 340px; max-width: 100%; height: auto; display: block; }
.in2-team-bio__li { width: 28px; height: 28px; }
.in2-team-bio__li img { width: 28px; height: 28px; }
.in2-team-bio__li img { display: block; transition: opacity var(--in2-duration-hover) var(--in2-ease); }
.in2-team-bio__li:hover img { opacity: 0.62; }
.in2-team-bio__close {
  position: absolute;
  right: 40px;
  top: 40px;
  width: 22px;
  height: 22px;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}
.in2-team-bio__close::before,
.in2-team-bio__close::after {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 22px;
  height: 2px;
  border-radius: 1px;
  background: var(--in2-gray);
}
.in2-team-bio__close::before { transform: rotate(45deg); }
.in2-team-bio__close::after { transform: rotate(-45deg); }
@media (max-width: 899px) {
  .in2-team-bio__inner { grid-template-columns: 1fr; padding: 40px 24px; }
  .in2-team-bio__aside { order: -1; }
}

/* ---------------------------------------------------------------------------
   Footer (figma 170:688)
   A CTA band over the contour-line artwork, then the 107px solid red bar.
   The plate is painted with multiply so its light ground drops away onto the page.
   --------------------------------------------------------------------------- */
.in2-footer { position: relative; overflow: hidden; background: var(--in2-white); }
.in2-footer__cta {
  position: relative;
  overflow: hidden;
  min-height: clamp(140px, 15.2vw, 219px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.in2-footer__cta::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 148.9%;
  background: url("assets/img/contour-lines.webp") center top / 100% auto no-repeat;
  mix-blend-mode: multiply;
  pointer-events: none;
}
.in2-footer__cta > * { position: relative; }
.in2-footer__bar {
  box-sizing: border-box;
  min-height: 107px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--in2-space-40);
  flex-wrap: wrap;
  padding: 24px clamp(24px, 6.9vw, 100px);
  background: var(--surface-footer);
  color: var(--in2-white);
  font-family: var(--in2-font-sans);
  font-weight: var(--in2-weight-medium);
  font-size: var(--in2-size-small);
  line-height: 100%;
}
.in2-footer__bar a { color: var(--in2-white); text-decoration: none; }
.in2-footer__bar a:hover { color: var(--in2-white); opacity: 0.75; }
.in2-footer__links { display: flex; gap: var(--in2-space-30); flex-wrap: wrap; }
.in2-footer__links a { text-transform: uppercase; font-weight: var(--in2-weight-semibold); }
.in2-footer__contact { display: flex; gap: var(--in2-space-20); flex-wrap: wrap; }
/* 1px white rule at 30% under the phone and email */
.in2-footer__contact > span { display: flex; flex-direction: column; gap: 3px; }
.in2-footer__contact > span::after { content: ""; height: 1px; background: var(--border-footer-rule); }

/* ---------------------------------------------------------------------------
   Standalone pill button.
   The footer CTA and any pattern-rendered link that is not a core/button block use
   this. Same geometry as the Button component: 60px, 2px inset ring, uppercase.
   --------------------------------------------------------------------------- */
.in2-btn {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: var(--in2-space-10);
  height: 60px;
  padding: 15px 25px 15px 15px;
  border-radius: var(--in2-radius-pill);
  border: 0;
  background: var(--surface-button);
  box-shadow: inset 0 0 0 2px var(--in2-red);
  color: var(--in2-red);
  font-family: var(--in2-font-sans);
  font-weight: var(--in2-weight-medium);
  font-size: var(--in2-size-button);
  line-height: 100%;
  letter-spacing: var(--in2-tracking-tight);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--in2-duration-hover) var(--in2-ease),
              color var(--in2-duration-hover) var(--in2-ease);
}
.in2-btn:hover { background: var(--in2-red); color: var(--in2-white); }
.in2-btn--primary::before {
  content: "";
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: var(--in2-radius-pill);
  background: var(--in2-red) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path d="M1.4 6.6 L6.6 1.4 M2.4 1.4 H6.6 V5.6" fill="none" stroke="white" stroke-width="1.1" stroke-linecap="square"/></svg>') center no-repeat;
  transition: background-color var(--in2-duration-hover) var(--in2-ease);
}
.in2-btn--primary:hover::before {
  background-color: var(--in2-white);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path d="M1.4 6.6 L6.6 1.4 M2.4 1.4 H6.6 V5.6" fill="none" stroke="%23AD2624" stroke-width="1.1" stroke-linecap="square"/></svg>');
}

/* ---------------------------------------------------------------------------
   Shared section rhythm. 96px vertical, gutter horizontal — the convention the
   reference value-props pattern establishes.
   --------------------------------------------------------------------------- */
.in2-section { padding: clamp(56px, 6.7vw, 96px) var(--in2-fluid-gutter); }
.in2-section > * { max-width: var(--in2-content-width); margin-inline: auto; }
.in2-section--cream { background: var(--in2-cream); }
/* The lifecycle diagram is drawn at 1440 wide (figma 194:936, "diagram", 1440x467) — its
   own 314.117px chevrons total ~1490px and never fitted the 1170 content column, which
   clipped "Commercialization". This section drops its gutters so the band runs at the full
   page width. Gutters come back below the breakpoint, where the bars are stacked and full
   width anyway. */
.in2-section--flush { padding-left: 0; padding-right: 0; }
.in2-section--flush > * { max-width: var(--in2-page-width); }
@media (max-width: 1099px) {
  /* matches the process band's own breakpoint — while it is stacked it wants normal gutters */
  .in2-section--flush { padding-left: var(--in2-fluid-gutter); padding-right: var(--in2-fluid-gutter); }
}
.in2-two-col { display: flex; flex-wrap: wrap; gap: clamp(40px, 7vw, 100px); align-items: flex-start; }
.in2-two-col > * { flex: 1 1 420px; min-width: 0; }

/* Contact with no form yet: the copy column should not sit at half width beside an
   empty gap. Once Gravity Forms is configured the panel returns and this drops away. */
.in2-two-col--single > * { flex: 1 1 100%; max-width: 760px; }

/* Why in2ition: heading left, "learn more" right, per the locked homepage —
   flex/space-between/flex-start with a 24px gap, and the row of props sits
   clamp(40px, 4.5vw, 65px) below it. */
.in2-why-head {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  justify-content: space-between;
  align-items: flex-start;
}
.in2-why .in2-value-props-wrap { margin-top: clamp(40px, 4.5vw, 65px); }

/* Why Us page: the nine-up grid sits 70px below the intro (Interior.jsx gap: 70). */
.in2-why-grid { margin-top: 70px; }
@media (max-width: 899px) { .in2-why-grid { margin-top: 48px; } }

/* Flex and GRID rows must opt out of WordPress' automatic block gap.
   Every container built from blocks needs listing here — .in2-why-grid was missed when it
   stopped being a PHP pattern and became real blocks, so its first card sat 24px above the
   other two in row one.
   `:where(.is-layout-flow) > * { margin-block-start: 24px }` is applied to every group,
   and inside a row-direction flex container that resolves to a TOP margin — so the second
   column of every two-column section sat 24px lower than the first. */
.in2-two-col > *,
.in2-cols-3 > *,
.in2-team-grid > *,
.in2-why-grid > *,
.in2-valueprop > *,
.in2-process > *,
.in2-process__stage > * { margin-block-start: 0; }

/* InfoPanel.jsx: "two panels sit side by side with a 40px gap and grow to equal height."
   .in2-two-col aligns to flex-start for copy columns, so the panels opt back in. */
.in2-two-col > .in2-panel { align-self: stretch; }

/* ===========================================================================
   MOBILE REFINEMENTS  (desktop review 2026-08-19)
   =========================================================================== */
@media (max-width: 899px) {
  /* The plate no longer overlaps the hero below the breakpoint, so it needs real air
     between the two instead of butting straight up against the artwork. */
  .in2-plate-overlap {
    margin-top: clamp(28px, 7vw, 48px);
    /* Full-bleed below the breakpoint. The container's gutter and the plate's own inset
       were stacking, so What We Do's copy started 48px from the edge while every section
       below it starts at the 24px gutter — a narrower measure that wraps differently and
       reads as a different type size. The plate now spans edge to edge and pads its own
       content to the same gutter, so all the copy on the page shares one left edge. */
    padding-left: 0;
    padding-right: 0;
  }
  .in2-plate {
    padding: clamp(28px, 7vw, 40px) var(--in2-fluid-gutter);
    /* No plate fill on mobile. The gradient starts at rgba(248,246,242,0.8) — a warm tint
       that reads as a hard bar across the top of the section once it meets the white page
       ground. On desktop the plate overlaps the hero artwork and the tint is what makes it
       a distinct surface; stacked on mobile it has nothing to sit on, so it just becomes a
       seam. */
    background: none;
  }

  /* Leadership goes back to `cover` below the breakpoint. The desktop rule is
     `100% auto` so the full width of the arrow plate shows un-zoomed, but that sizes the
     artwork off the WIDTH — at 402px it is only ~128px tall, leaving most of a much
     taller box painted flat cream. Cover crops the sides instead, which is the right
     trade when the box is tall and narrow. The min-height is desktop-only for the same
     reason.

     Better still would be the mobile crop: readme.md notes the file annotates "Mobile
     displays different images" and says to pull the `* - mobile` frame's own artwork
     rather than scaling the desktop export. That asset was not in the export. */
  .in2-leadership {
    min-height: 0;
    background-size: cover;
    /* Right-aligned, not centred. The arrows live on the right of the 2200x700 plate;
       cover on a narrow box crops horizontally, so centring threw exactly them away and
       left a flat pastel wash. Anchoring right keeps the arrows in frame. */
    background-position: right center;
  }

  /* Two portraits per row. The cards are a fixed 340px on desktop, which at 402px wide
     leaves room for exactly one — a grid lets them share the row and scale down. */
  .in2-team-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
  }
  /* height:100% so both cards in a row end level. Grid stretches the ITEM, but the cream
     panel only grows to its content, so a two-line role next to a four-line one left one
     card visibly short. */
  .in2-team-card { width: auto; height: 100%; display: flex; flex-direction: column; }
  .in2-team-card__trigger { display: flex; flex-direction: column; height: 100%; }
  .in2-team-card__plate { flex: 1; }
  /* The 34px desktop name-plate inset is too generous at half width. */
  .in2-team-card__plate { padding: 16px 16px 20px; }
  .in2-team-card__name { font-size: 16px; }
  .in2-team-card__role { font-size: 14px; }

  /* Footer: stack the three groups instead of wrapping them unevenly. */
  .in2-footer__bar {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--in2-space-25);
    padding: var(--in2-space-30) var(--in2-gutter-mobile);
  }
  .in2-footer__links { flex-direction: column; align-items: center; gap: var(--in2-space-15); }
  .in2-footer__contact { flex-direction: column; align-items: center; gap: var(--in2-space-15); }
  /* The phone/email underline is a block rule under each item, so it has to be told to
     match the text width once the column is centred — otherwise it stretches full width. */
  .in2-footer__contact > span { align-items: center; }
  /* Copyright reads as a sign-off, so it goes last rather than first. */
  .in2-footer__bar > span:first-child { order: 3; }
  .in2-footer__links { order: 1; }
  .in2-footer__contact { order: 2; }
}

/* ---------------------------------------------------------------------------
   Interior hero under the opaque desktop bar.

   Deliberately at the end of the file: it overrides `.in2-page-hero` above at equal
   specificity, so source order is what makes it win. Moving it earlier silently
   disables it.

   With the header opaque, 133.5px of the 246px band is painted over — only 112px was
   left and the title's ascenders were clipped by the bar. The band now delivers its
   full height BELOW the header. aspect-ratio must be cleared or it keeps deriving the
   old height and fights min-height.

   Applies at every width. Mobile's bar is transparent so nothing was being clipped, but
   the band there is only its 120px floor and the title was landing 4px INTO the bar,
   sitting under the burger. Reserving the header height fixes both cases with one rule.
   --------------------------------------------------------------------------- */
.in2-page-hero {
  aspect-ratio: auto;
  padding-top: var(--in2-nav-current);
  min-height: calc(var(--in2-nav-current) + clamp(120px, 16.5vw, 246px));
}

/* ---------------------------------------------------------------------------
   The three formerly-PHP sections are now editable blocks, so a few rules that used
   to live in pattern markup move here.
   --------------------------------------------------------------------------- */
/* core/image wraps every icon in a <figure> that brings its own margins */
.in2-valueprop figure { margin: 0; }
/* Why Us headings are 24px; the homepage row is 32px via the card-title preset. Set in
   CSS rather than per block, because theme.json disables custom font sizes — which is the
   guardrail keeping the client on the type scale. */
.in2-why-grid .in2-valueprop h3 { font-size: clamp(20px, 1.7vw, 24px); line-height: 1.2; }
/* The chevron label is a paragraph block now, so neutralise the block's own margins. */
.in2-process__bar { margin: 0; }

/* A fixed bar means the open mobile menu is fixed too, so it must not run past the
   viewport on a short screen. */
@media (max-width: 899px) {
  .in2-mobile-panel.is-open {
    max-height: calc(100vh - var(--in2-nav-height-mobile));
    overflow-y: auto;
  }
}
