/*
  The Lawneds, component layer
  Twenty two components (section 11). If a design comp introduces a twenty
  third, challenge it. Authority: plans/lawneds-brand-system.md
*/

/* ==========================================================================
   1. Buttons (section 11.1)
   All buttons use --accent-strong. --accent is never a button fill with white
   text: it is 4.25:1 and fails AA. This is the single most important
   correction in the brand system (section 4.3 rule 2).
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  font-family: var(--font-body);
  font-size: var(--fs-button);
  font-weight: 600;
  line-height: var(--lh-button);
  letter-spacing: var(--ls-button);
  padding: var(--space-16) var(--space-32);
  border-radius: var(--radius);      /* 8px. No pill buttons (section 7.1). */
  text-decoration: none;
  min-height: 44px;                  /* touch target (section 12.1) */
  transition:
    transform var(--dur-micro) var(--ease),
    box-shadow var(--dur-micro) var(--ease),
    background-color var(--dur-micro) var(--ease),
    color var(--dur-micro) var(--ease);
}

/* Primary */
.btn--primary {
  background: var(--accent-strong);
  color: var(--white);              /* 5.78:1 */
  border: 1px solid var(--accent-strong);
}
.btn--primary:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: var(--shadow-hover);
  background: color-mix(in srgb, var(--accent-strong) 94%, black);
  border-color: color-mix(in srgb, var(--accent-strong) 94%, black);
}
.btn--primary:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: none;
}

/* Secondary */
.btn--secondary {
  background: transparent;
  border: 1px solid var(--navy);
  color: var(--navy);
}
.btn--secondary:hover:not(:disabled) {
  background: var(--navy);
  color: var(--white);
  transform: translateY(-1px);
  box-shadow: var(--shadow-hover);
}
.btn--secondary:active:not(:disabled) { transform: translateY(0); box-shadow: none; }

/* On navy (hero, CTA band). Primary is unchanged: accent-strong reads
   correctly on navy. Secondary inverts. */
.btn--on-navy {
  background: transparent;
  border: 1px solid var(--white);
  color: var(--white);
}
.btn--on-navy:hover:not(:disabled) {
  background: var(--white);
  color: var(--navy);
  transform: translateY(-1px);
}

/* Disabled */
.btn:disabled,
.btn[aria-disabled='true'] {
  background: var(--border-strong);
  border-color: var(--border-strong);
  color: var(--slate);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* Loading (section 11.1, amended in brand system 1.1)
   Label swaps to "Sending", width held, aria-busy + aria-disabled.
   NO SPINNER. NO MOTION.

   The spinner was removed from the system, not merely from this file: section
   10.1 permits nothing that repeats and section 10.3 prohibits spinning, so a
   spinner was the one device both rules forbade. Consequences worth keeping in
   view if anyone is tempted to put it back:

     - With no motion there is no reduced-motion special case, and therefore no
       way for the state to be conveyed by motion alone.
     - aria-disabled (not disabled) keeps the button in the tab order, so focus
       is not lost mid-submit, while making it inert against double submission.
     - The width is held by the label swap itself, so the row cannot reflow.

   The template swaps the label text. This file only holds the width and makes
   the button inert. */
.btn[aria-busy='true'] {
  cursor: progress;
  pointer-events: none;
}
.btn[aria-busy='true'] .btn__label { opacity: 0.7; }

/* Text link with arrow (section 11.1). Arrow translates 4px right, 150ms. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  color: var(--accent-strong);
  font-weight: 600;
  text-decoration: none;
  min-height: 44px;
}
.link-arrow .icon { color: currentColor; width: 16px; height: 16px; transition: transform var(--dur-link) var(--ease); }
.link-arrow:hover .icon { transform: translateX(4px); }
.link-arrow:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ==========================================================================
   Logo, text setting (section 3)
   INTERIM. Section 3.1 requires three drawn variants: primary navy wordmark,
   reversed white, and a square mark for favicon and avatar. None exist yet.
   Until they do, the wordmark is set in Manrope 700, which is the firm's own
   heading face, so it reads as deliberate rather than missing.

   Section 3.2 minimums are honoured: 140px desktop, 110px mobile, mark 32px.
   The header swaps primary to reversed on scroll, and section 3.1 warns both
   files must be preloaded or the swap will flash. Text has no such problem,
   which is a small argument for keeping this even after the logo lands.
   ========================================================================== */

.logo {
  display: inline-flex;
  align-items: baseline;
  gap: 0.28em;
  min-width: 110px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 21px;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--navy);
  text-decoration: none;
  white-space: nowrap;
}
@media (min-width: 1024px) { .logo { min-width: 140px; font-size: 24px; } }

.logo__the {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.58em;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--slate);
}
/* Reversed, for navy grounds. Slate is 1.74:1 on navy and is never type there
   (section 4.3 rule 3), so the article mark lifts to accent-light at 5.14:1. */
.logo--reversed { color: var(--white); }
.logo--reversed .logo__the { color: var(--accent-light); }

/* Logo, image variant. The supplied artwork carries its own navy field, so it
   sits as a rounded tile rather than as type. That is why none of the colour
   state rules above apply to it: there is no text to recolour, and the tile
   reads the same on white, on off-white and over the hero.
   Sized in CSS with width and height on the img as well, so it reserves its
   space before it loads and contributes nothing to CLS. */
.logo--img { min-width: 0; gap: 0; font-size: 0; line-height: 0; }
.logo__img { display: block; width: 144px; height: auto; }
.logo__img--lockup { width: 180px; }

@media (max-width: 639px) {
  .logo__img { width: 124px; }
  .logo__img--lockup { width: 160px; }
}

/* Mark only, 32px square. Favicon, app icon, social avatar. */
.logo-mark {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: var(--navy);
  color: var(--white);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -0.03em;
}

/* ==========================================================================
   Pending states (rebuild plan 2.3)
   For content that is BLOCKED ON THE CLIENT, not merely unwritten. These render
   as a deliberate, designed absence rather than as a broken component or, worse,
   as an invented figure. Section 2.1: never write a number the client has not
   confirmed. The live site animates counters from zero to nothing; this is the
   honest alternative to that.
   ========================================================================== */

.is-pending { position: relative; }

/* A muted rule standing in for the numeral. Reads as "awaiting", not as data. */
.stat-block--pending .stat-block__value {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  color: rgb(255 255 255 / 38%);
}
.stat-block--pending .stat-block__value::before {
  content: '';
  display: block;
  width: 64px;
  height: 4px;
  border-radius: 2px;
  background: currentColor;
}
.pending-note {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  margin-top: var(--space-8);
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-light);
}
.pending-note::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
/* On light grounds the note takes accent-strong: accent-light is for navy only. */
.pending-note--on-light { color: var(--accent-strong); }

/* Skeleton lines for a blocked engagement summary. Static: no shimmer, no
   pulse. Section 10.1 permits three animations and this is not one of them. */
.skeleton-line {
  display: block;
  height: 8px;
  border-radius: 2px;
  background: var(--border);
  margin-bottom: var(--space-8);
}
.skeleton-line--short { width: 45%; }
.skeleton-line--mid { width: 72%; }

/* ==========================================================================
   2. eyebrow-label (section 11.2)
   The single most useful typographic device in this system. Uppercase is a
   CSS transform, never typed caps (section 2.2: never ALL CAPS).
   ========================================================================== */

.eyebrow {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  line-height: var(--lh-eyebrow);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--slate);
  margin-bottom: var(--space-16);
}
.eyebrow--accent { color: var(--accent-strong); }
/* Emerald on navy is 2.73:1, so accent-light (5.14:1) carries it instead. */
.band--navy .eyebrow { color: var(--accent-light); }

/* The CTA band's eyebrow is WHITE, and it is the one place in this system the
   eyebrow is neither slate nor accent (section 5.4). Scanned, not chosen:

     slate         1.01:1   invisible. This is what shipped, on all 24
                            templates, and it is the exact case CLAUDE.md
                            rule 4 names: "Slate is never type on navy".
     accent-light  2.48:1   still fails. accent-light's 5.14:1 is measured on
                            FLAT navy; this band is a photograph under a 65%
                            overlay, and the fog in cta-bridge-fog-sunrise is
                            far lighter than flat navy.
     white         5.60:1   passes 4.5:1 at 13px.

   The cause is the same one that made the eyebrow slate to begin with:
   `.cta-band` is a shared partial, not a `.band--navy` (section 11.0), so
   every on-navy override in this file misses it. White is also what the rest
   of the band already uses, `.cta-band h2` and `.cta-band p` below, so this
   makes the eyebrow consistent with its own band rather than exceptional.

   FLAG FOR THE BRAND OWNER: section 5.4 says the eyebrow is "slate or accent
   coloured", and section 13.3 wants a deviation recorded rather than absorbed.
   Recording it here. The alternative is a darker CTA image, which is the same
   regrade the six sector heroes need. */
.cta-band .eyebrow { color: var(--white); }

/* ==========================================================================
   3. section-header: eyebrow + H2 + lead (section 11.2)
   ========================================================================== */

.section-header { max-width: 62ch; }
.section-header > h2 + .lead { margin-top: var(--space-24); color: var(--slate); }
.band--navy .section-header > h2 + .lead { color: rgb(255 255 255 / 90%); }

/* ==========================================================================
   4. Cards, shared (section 7.4)
   Flat at rest with a hairline border. Lift only on hover. There are no
   resting shadows: shadow is a state, not a decoration (section 7.3).
   ========================================================================== */

.card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-24);
  transition:
    transform var(--dur-micro) var(--ease),
    box-shadow var(--dur-micro) var(--ease),
    border-color var(--dur-micro) var(--ease);
}
@media (min-width: 1024px) { .card { padding: var(--space-32); } }

/* On white bands the card is off-white, on off-white bands it is white
   (section 7.4). */
.band--white .card { background: var(--off-white); }
.band--off-white .card { background: var(--white); }

.card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-hover);
  border-color: var(--border-strong);
}
.card p { max-width: var(--measure-card); }

/* 5. solution-card: icon, title, strapline, extract, capability count, link */
.solution-card { display: flex; flex-direction: column; gap: var(--space-16); }
.solution-card .icon { color: var(--accent-strong); }
/* The strapline is named in the component spec and was being carried by a bare
   <strong> with a class that had no rule behind it. Navy, weight 500: it is a
   sub-heading, not emphasis inside a sentence. */
.solution-card__strap { color: var(--navy); font-weight: 500; }
.solution-card__count { color: var(--slate); font-size: var(--fs-caption); }
.solution-card .link-arrow { margin-top: auto; }

/* 6. sector-tile: image, navy overlay on hover, name, arrow.
   Overlay is flat navy, never a gradient (section 4.5). 4:3 (section 9.6). */
.sector-tile {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius);
  text-decoration: none;
  isolation: isolate;
}
.sector-tile img { width: 100%; height: 100%; object-fit: cover; }
.sector-tile::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--overlay-tile);
  transition: background var(--dur-micro) var(--ease);
}
.sector-tile:hover::after { background: var(--overlay-tile-hover); }
.sector-tile__label {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  padding: var(--space-24);
  color: var(--white);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-h4);
}
.sector-tile__label .icon { color: var(--white); transition: transform var(--dur-link) var(--ease); }
.sector-tile:hover .sector-tile__label .icon { transform: translateX(4px); }

/* 7. person-card: portrait 3:4, name, role, specialism, bio (section 9.6)
   The block itself carried no rule while its elements did, so the class was
   dead markup. Bio length is deliberately unbalanced (the Executive Principal
   has four paragraphs, the others two) and the card is built to tolerate that
   rather than pad the shorter ones. */
.person-card { display: flex; flex-direction: column; }
.person-card .pending-note { margin-top: auto; padding-top: var(--space-16); }

/* 4:5, not the 3:4 of section 9.6, and this is a deliberate amendment.

   THE FILE DIMENSIONS DO NOT SET THE RENDERED HEIGHT. The card is fluid, so the
   height is aspect-ratio multiplied by whatever width the grid gives it. At the
   1100px content maximum, three across with 24px gutters, each card is ~350px:

       3 / 4  ->  467px tall   (section 9.6 as written)
       4 / 5  ->  437px tall   (here)
       1 / 1  ->  350px tall
       2 / 3  ->  525px tall   <- what a literal 400x600 box would give

   Worth stating because it is counterintuitive: exporting the file at 400x600
   makes it TALLER, not shorter, if the box follows the file. 400x600 is 2:3, a
   taller ratio than the 3:4 it replaced. The files are 400x600 because that is
   the right source size for this slot, and the box is 4:5 because that is what
   reduces the height.

   object-fit: cover with object-position at the top takes the crop off the
   bottom, so the head-and-shoulders framing survives. */
.person-card__portrait {
  aspect-ratio: 2 / 3;
  width: 100%;
  object-fit: cover;
  object-position: 50% 15%;
  border-radius: var(--radius);
  background: var(--border);
}
.person-card__name { margin-top: var(--space-24); }
.person-card__role { color: var(--accent-strong); font-weight: 500; }
.person-card__specialism { color: var(--slate); font-size: var(--fs-small); }
.person-card__bio { margin-top: var(--space-16); }
.person-card__bio p + p { margin-top: var(--space-16); }

/* ---- Bio disclosure ----------------------------------------------------
   The bios are deliberately unequal: the Executive Principal has four
   paragraphs, the other two have two each (information architecture 7: "Either
   level them up or accept the hierarchy deliberately. Design the card to
   tolerate both."). Padding the short ones to match would be inventing copy;
   truncating the long one would throw away the only substantive track record
   the firm can currently evidence.

   So the card shows the FIRST PARAGRAPH ONLY, which is three to four lines for
   all three people, and puts the remainder behind a disclosure. Collapsed, the
   three cards are equal. Expanded, the hierarchy is intact and honest.

   Mechanism is the accordion's (component 14): grid-template-rows 0fr to 1fr
   animates to auto height with nothing measured in JS, at the 250ms the system
   specifies. content-visibility keeps the collapsed remainder out of the
   accessibility tree and out of the tab order, which a zero-height grid row
   does NOT do on its own: without it a screen reader reads a bio the toggle
   says is collapsed. Same defect, same fix, as the accordion. */

.person-card__more {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-accordion) var(--ease);
}
.person-card__more[data-open='true'] { grid-template-rows: 1fr; }
.person-card__more > div {
  overflow: hidden;
  content-visibility: hidden;
}
.person-card__more[data-open='true'] > div { content-visibility: visible; }
.person-card__more > div > p:first-child { margin-top: var(--space-16); }

.person-card__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  min-height: 44px;                 /* touch target, section 12.1 */
  margin-top: var(--space-8);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--accent-strong);      /* 5.78:1, the interactive colour */
}
.person-card__toggle:hover { text-decoration: underline; text-underline-offset: 3px; }
.person-card__toggle .icon {
  color: currentColor;
  width: 16px;
  height: 16px;
  transition: transform var(--dur-accordion) var(--ease);
}
.person-card__toggle[aria-expanded='true'] .icon { transform: rotate(180deg); }

/* The label swaps by CSS so the template carries both strings and the JS only
   toggles state. Copy deck 1.6 fixes the button labels; "Read more" is in that
   set. "Read less" is the one addition, recorded in brand system 1.4. */
.person-card__toggle .is-more { display: inline; }
.person-card__toggle .is-less { display: none; }
.person-card__toggle[aria-expanded='true'] .is-more { display: none; }
.person-card__toggle[aria-expanded='true'] .is-less { display: inline; }

/* 8. value-card: icon, title, one sentence. No border, no background. */
.value-card {
  background: none;
  border: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}
.value-card:hover { transform: none; box-shadow: none; border: 0; }
/* Slate, not accent-strong: section 8.1 makes slate the default and reserves
   accent-strong for interactive. A value-card is a static display with no
   border, no background and no control in it. */
.value-card .icon { color: var(--slate); }

/* 9. stat-block: large numeral Manrope 700 48px, label beneath.
   Never a counter animating to an unverified number (section 10.3). */
.stat-block { display: flex; flex-direction: column; }

.stat-block__value {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 48px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--white);
}
.stat-block__label {
  margin-top: var(--space-8);
  color: rgb(255 255 255 / 90%);
  font-size: var(--fs-small);
}

/* 10. capability-list: two column, check icon, 17px */
.capability-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-16) var(--gutter);
  font-size: var(--fs-body-lg);
}
@media (min-width: 640px) { .capability-list { grid-template-columns: 1fr 1fr; } }
.capability-list li { display: flex; align-items: flex-start; gap: var(--space-16); }
/* Slate per section 8.1 default: a checklist is static, not interactive. */
.capability-list .icon { color: var(--slate); margin-top: var(--space-4); }

/* 11. insight-card: image 16:9, category pill, title, excerpt, Read More */
.insight-card { display: flex; flex-direction: column; gap: var(--space-16); padding: 0; overflow: hidden; }
.insight-card__img { aspect-ratio: 16 / 9; width: 100%; object-fit: cover; background: var(--border); }
.insight-card__body { padding: var(--space-24); display: flex; flex-direction: column; gap: var(--space-16); flex: 1; }
.insight-card .link-arrow { margin-top: auto; }

/* pill-list: the category filter and the sector-breadth cloud. Six pills, not a
   dropdown (information architecture 8). Was carrying its layout in an inline
   style behind a class with no rule; the layout belongs here. */
.pill-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* pill: shared by insight category and engagement sector. Radius 4px:
   nothing is fully rounded (section 7.1), so this is a tag, not a capsule.

   NOTE, unresolved: the pill is the only navigation component in the 22 with no
   aria-current state. breadcrumb and pagination both style theirs. On a category
   archive the current category is announced to a screen reader but not shown to
   a sighted user, which is a 1.4.1 use-of-colour problem in reverse. Adding a
   state is a system change (13.3), so it is recorded rather than invented. */
.pill {
  display: inline-block;
  align-self: flex-start;
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-strong);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  padding: var(--space-4) var(--space-8);
  border-radius: var(--radius-sm);
}

/* 12. insight-card-featured: image left 7 cols, text right 5 cols */
.insight-featured { display: grid; grid-template-columns: 1fr; gap: 0; padding: 0; overflow: hidden; }
@media (min-width: 1024px) {
  .insight-featured { grid-template-columns: 7fr 5fr; }
  .insight-featured__img { height: 100%; }
}
.insight-featured__img { aspect-ratio: 16 / 9; width: 100%; object-fit: cover; background: var(--border); }
.insight-featured__body { padding: var(--space-32); display: flex; flex-direction: column; gap: var(--space-16); justify-content: center; }

/* 13. engagement-card: sector pill, challenge, action, outcome, sector link.
   Blocked on real client engagements (rebuild plan 2.3). */
.engagement-card { display: flex; flex-direction: column; gap: var(--space-16); }
.engagement-card dt {
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--slate);
  margin-bottom: var(--space-4);
}
.engagement-card dd { margin: 0; }
.engagement-card dd + dt { margin-top: var(--space-16); }
.engagement-card__outcome { font-weight: 500; color: var(--navy); }

/* 14. process-step: numeral, title, body. Three across. */
.process-step__num {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-h2);
  color: var(--accent);        /* decorative, large: permitted use of --accent */
  line-height: 1;
  display: block;
  margin-bottom: var(--space-16);
}
.process-step h3 { margin-bottom: var(--space-8); }

/* 15. accordion: chevron, 250ms, one open at a time */
.accordion { border-top: 1px solid var(--border); }
.accordion__item { border-bottom: 1px solid var(--border); }
.accordion__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  width: 100%;
  text-align: left;
  padding: var(--space-24) 0;
  min-height: 44px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-h4);
  color: var(--navy);
}
.accordion__trigger .icon { transition: transform var(--dur-accordion) var(--ease); }
.accordion__trigger[aria-expanded='true'] .icon { transform: rotate(180deg); }
/* grid-template-rows 0fr to 1fr animates to auto height with no JS measuring.
   But 0fr gives zero HEIGHT, not removal: without the content-visibility rule
   below, a collapsed panel stays in the accessibility tree and any link inside
   it stays tabbable, while aria-expanded="false" promises the opposite.
   content-visibility: hidden removes it from both, and unlike `hidden`/`display:
   none` it does not break the height transition. */
.accordion__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-accordion) var(--ease);
}
.accordion__panel[data-open='true'] { grid-template-rows: 1fr; }
.accordion__panel > div {
  overflow: hidden;
  content-visibility: hidden;
}
.accordion__panel[data-open='true'] > div { content-visibility: visible; }
.accordion__panel p { padding-bottom: var(--space-24); max-width: var(--measure-card); }

/* 16. form-field: label above, input, helper, error (section 11.3)
   Labels are real <label> elements above inputs. Placeholder text is not a
   label: the most common accessibility failure in contact forms. */
.field { display: flex; flex-direction: column; gap: var(--space-8); }
.field__label { font-weight: 500; font-size: var(--fs-small); color: var(--navy); }
.field__hint { font-size: var(--fs-caption); color: var(--slate); }
.field__control {
  width: 100%;
  padding: var(--space-16);
  min-height: 44px;
  font-size: var(--fs-body);
  color: var(--charcoal);
  background: var(--white);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur-micro) var(--ease);
}
.field__control:focus-visible { border-color: var(--accent-strong); }
.field__control:disabled { background: var(--off-white); color: var(--slate); cursor: not-allowed; }

/* Select: remove the inconsistent native control and draw the brand chevron
   (Lucide chevron-down, slate) so the dropdown looks the same on every platform.
   The option list itself stays native, which is the accessible default. */
select.field__control {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: calc(var(--space-24) + var(--space-16));
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%233e5f7e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat: no-repeat;
  background-position: right var(--space-16) center;
  background-size: 20px 20px;
  cursor: pointer;
}
select.field__control::-ms-expand { display: none; }
select.field__control:invalid { color: var(--slate); }  /* the empty "Please choose" prompt reads as placeholder */

/* 23. WhatsApp FAB. Floating pill, bottom-right, brand accent-strong (not green,
   client decision). Rendered only when a real number is set (hid_render_fab).
   Bottom-anchored, so the admin bar never affects it. */
.fab--whatsapp {
  position: fixed;
  right: var(--space-24);
  bottom: var(--space-24);
  z-index: 70;
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-16) var(--space-24);
  min-height: 56px;
  background: var(--accent-strong);
  color: var(--white);
  border-radius: 999px;
  box-shadow: 0 6px 20px rgb(18 58 99 / 25%);
  text-decoration: none;
  font-weight: 600;
  font-size: var(--fs-small);
  line-height: 1;
  transition: transform var(--dur-micro) var(--ease), box-shadow var(--dur-micro) var(--ease);
}
.fab--whatsapp .fab__icon { width: 24px; height: 24px; flex-shrink: 0; }
.fab--whatsapp:hover { transform: translateY(-2px); box-shadow: 0 10px 28px rgb(18 58 99 / 30%); color: var(--white); }
.fab--whatsapp:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--white), 0 0 0 6px var(--accent-strong), 0 6px 20px rgb(18 58 99 / 25%);
}
@media (prefers-reduced-motion: reduce) {
  .fab--whatsapp { transition: none; }
  .fab--whatsapp:hover { transform: none; }
}
/* Phones: collapse to a circular icon-only button; the label stays for assistive
   tech only. */
@media (max-width: 640px) {
  .fab--whatsapp { padding: 0; width: 56px; justify-content: center; }
  .fab--whatsapp .fab__label {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}
.field--error .field__control { border-color: var(--error); }
.field__error {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  color: var(--error);            /* 6.54:1 on white */
  font-size: var(--fs-caption);
  font-weight: 500;
}
/* circle-alert, added to section 8.3 in brand system 1.1 for exactly this role.
   Previously reused `x`, which is assigned to Close, so an error message
   carried a dismiss affordance. 20px is the section 8.1 inline size. */
.field__error .icon { color: currentColor; width: var(--icon-size-inline); height: var(--icon-size-inline); }

/* Error summary on submit (section 12.1) */
.error-summary {
  border: 1px solid var(--error);
  border-left-width: 4px;
  border-radius: var(--radius-sm);
  padding: var(--space-24);
  background: var(--white);
}
/* Inter 600, not 700: the authorised body weights are 400, 500 and 600, and 600
   is the only weight this system added (section 5.1). Without the explicit
   weight this inherits 700 from the base h3 rule, which is unauthorised and
   outside the variable font's declared 400-600 range, so it would synthesise. */
.error-summary h3 {
  color: var(--error);
  font-family: var(--font-body);
  font-size: var(--fs-h4);
  font-weight: 600;
}
.error-summary ul { margin-top: var(--space-16); display: flex; flex-direction: column; gap: var(--space-8); }
.error-summary a { color: var(--error); }

/* 17. breadcrumb: 14px, slate, chevron separators */
.breadcrumb { font-size: var(--fs-caption); }
.breadcrumb ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8); list-style: none; margin: 0; padding: 0; }
.breadcrumb li { display: flex; align-items: center; gap: var(--space-8); color: var(--slate); }
.breadcrumb .icon { width: 14px; height: 14px; color: var(--border-strong); transform: rotate(-90deg); }
/* 44px touch target per section 12.1, like every other interactive component in
   this file. WCAG 2.2 SC 2.5.8 would permit 24px and arguably exempts inline
   links, but section 12.1 states a flat 44 and the brand system is the
   authority. Inline-flex so the box exists without disturbing the row. */
.breadcrumb a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--slate);
  text-decoration: none;
}
.breadcrumb a:hover { text-decoration: underline; }
.breadcrumb [aria-current='page'] { color: var(--charcoal); font-weight: 500; }

/* 18. pagination: numbered. Never infinite scroll. */
.pagination ol { display: flex; flex-wrap: wrap; gap: var(--space-8); list-style: none; margin: 0; padding: 0; }
.pagination a, .pagination span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding-inline: var(--space-8);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--navy);
  transition: border-color var(--dur-micro) var(--ease), background var(--dur-micro) var(--ease);
}
.pagination a:hover { border-color: var(--border-strong); background: var(--off-white); }
.pagination [aria-current='page'] { background: var(--accent-strong); border-color: var(--accent-strong); color: var(--white); font-weight: 600; }

/* 18. related-grid: three cards, section header.
   "Hides when empty" is a TEMPLATE responsibility, not a CSS one, per brand
   system 11.2 as clarified in 1.1. The rule that used to live here,
   `.related-grid:empty { display: none }`, NEVER FIRED: `:empty` requires zero
   child nodes including whitespace, and a PHP template emits a newline between
   its tags. It looked correct, passed review, and did nothing. Deleted rather
   than left in place, because a rule that implies a guarantee it cannot keep is
   worse than no rule.

   Wrap the component in have_posts() and do not render the container at all. */
.related-grid { display: grid; gap: var(--gutter); grid-template-columns: 1fr; }
@media (min-width: 1024px) { .related-grid { grid-template-columns: repeat(3, 1fr); } }

/* 20. closing-statement (section 11.2)
   The most characteristic thing in this system. Manrope 700 at H3, navy,
   centred, 96px vertical padding, white band, no image, no button. One per
   page, immediately before the CTA band. Never body copy. */
.closing-statement {
  background: var(--white);
  padding-block: var(--space-96);
  text-align: center;
}

/* THE COPY HAS TWO PARTS AND THEY ARE NOT THE SAME BEAT.

   Every closing statement in the deck is N short declarative lines followed, after
   a blank line, by one resolving sentence:

       Strong projects create value.
       Strong organisations create opportunity.
       Strong partnerships create lasting success.

       That is the future we continue building together.

   An earlier draft rendered all of it as one flat list at Manrope 700 / H3 in a
   30ch column. That did two things wrong. It threw away the stanza break the deck
   marks deliberately. And it set the resolution in the same heavy display face:
   on the Solutions page that is a 180 character sentence at 26px bold in a 30ch
   measure, roughly six lines of solid weight, which is a wall rather than a
   resolution. The declaratives are "the tightest writing in the whole document"
   (11.2) and the resolution's job is to let them land, not to compete.

   So: the declaratives keep the specified Manrope 700 at H3. The resolution drops
   to Inter 400 at lead size in slate, on a wider measure, with air above it. The
   contrast between the two is the device. Restraint carries the confidence. */

.closing-statement__lines {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.35;
  color: var(--navy);
  /* Wide enough that each declarative holds ONE line at 26px: the longest in the
     deck is 43 characters, and the old 30ch forced them to wrap, which destroys
     the stanza. Balance handles the mobile step down. */
  max-width: 46ch;
  margin-inline: auto;
  text-wrap: balance;
}
.closing-statement__lines p + p { margin-top: var(--space-4); }

.closing-statement__resolution {
  margin-top: var(--space-32);
  margin-inline: auto;
  max-width: 58ch;
  font-family: var(--font-body);
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  font-weight: 400;
  color: var(--slate);          /* 6.68:1 on white */
  text-wrap: pretty;
}

/* A single line with no resolution is the pure form of the component: the
   homepage carries the tagline alone. It takes the display treatment and needs
   no measure constraint fighting it. */
.closing-statement__lines:only-child { max-width: 52ch; }

/* 21. cookie-banner: bottom, navy, blocks scripts before consent */
.cookie-banner {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 90;
  background: var(--navy);
  color: var(--white);
  padding: var(--space-24);
}
.cookie-banner__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
  align-items: flex-start;
}
@media (min-width: 1024px) {
  .cookie-banner__inner { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--space-32); }
}
.cookie-banner p { max-width: 70ch; font-size: var(--fs-small); color: rgb(255 255 255 / 90%); }
.cookie-banner a { color: var(--accent-light); }   /* emerald on navy is 2.73:1 */
.cookie-banner__actions { display: flex; gap: var(--space-16); flex-wrap: wrap; }

/* 22. hero and CTA band overlays (section 4.5)
   Flat navy at fixed opacity. A gradient here reads as consumer, not
   institutional. Hero content never animates in on load (section 10.3). */
.hero, .cta-band {
  position: relative;
  isolation: isolate;
  color: var(--white);
}
.hero__media, .cta-band__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero::after, .cta-band::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
}
.hero::after     { background: var(--overlay-hero); }   /* 60% */
.cta-band::after { background: var(--overlay-cta); }    /* 65% */

/* Band padding (section 6.4): 64 mobile, 96 tablet, 128 desktop.
   `--band-padding` already carries those three values, so this is the same
   rhythm every other band on the page keeps, not a number invented here.

   THIS WAS MISSING AND SHIPPED ON ALL 24 TEMPLATES. The CTA band computed to
   `padding: 0` at every breakpoint, so the heading, the subline and the button
   ran into the band edges on every page of the site. It is easy to see how:
   `.band` carries `padding-block: var(--band-padding)` and the CTA is not a
   `.band`, it is a shared partial (section 11.0). The heroes never exposed the
   same hole because `.hero--home` and `.page-hero` both bring their own
   padding, which is also why this rule is scoped to `.cta-band` alone. Adding
   it to `.hero` as well would double pad every composed `hero page-hero`.

   Evidence the gap was known and worked around rather than fixed: the
   styleguide's standalone `.hero` demo hard codes `style="padding: var(
   --space-64) var(--space-32)"` inline, because without it the demo collapses
   too. */
.cta-band { padding-block: var(--band-padding); }

.hero h1, .cta-band h2 { color: var(--white); }

/* PURE WHITE, not white at 90%, and this is load bearing.
   An earlier draft dimmed these sublines to rgb(255 255 255 / 90%) for cosmetic
   softness. On flat navy that is harmless: 11.60:1 has room to give away. Over a
   PHOTOGRAPH under a 60 or 65 per cent overlay it is not, because the starting
   point is already near the threshold, and the 90 per cent costs roughly 0.5:1.

   Measured, worst text-sized region, every image band on the site:

                                    white@90%     pure white
     Home hero                        4.12 FAIL     4.65 pass
     About hero                       4.25 FAIL     4.80 pass
     Sector heroes (six)         4.05 to 4.39 FAIL  4.57 to 4.98 pass
     CTA band (every page)            4.36 FAIL     4.94 pass

   Twelve image bands, every one failing AA on a 21px subline, from one cosmetic
   decision. The fix is not to regrade twelve photographs. It is to stop dimming
   white text that is already fighting for contrast.

   Flat navy surfaces keep their 90 per cent: see .page-hero__sub, the footer and
   the cookie banner, all of which sit on --navy and have the headroom. */
.hero p, .cta-band p { color: var(--white); }
.hero__actions, .cta-band__actions { display: flex; flex-wrap: wrap; gap: var(--space-16); margin-top: var(--space-32); }
