/* ============================================================
   Northumberland Humane Society — component stylesheet  v1.0
   Northumberland Humane Society · Big Red Truck · Foxpress

   Consumes nhs-tokens.css. Load order matters:
     <link rel="stylesheet" href="https://use.typekit.net/nbm2jes.css">
     <link rel="stylesheet" href="nhs-tokens.css">
     <link rel="stylesheet" href="nhs-styles.css">

   Every rule cites the NHS Components section it implements.
   A number lives in ONE place: change the spec and this file together.
   Naming: nhs- prefix, block__element--modifier, lowercase.

   Two things this file deliberately does NOT do:
   - It does not restate token values. If a number is in nhs-tokens.css,
     reference the variable; a literal here is a future contradiction.
   - It does not style icons' colour. Icons inherit currentColor from the
     component, which is the whole reason they are inlined per instance.
   ============================================================ */


/* ------------------------------------------------------------
   Base
   ------------------------------------------------------------ */

/* The Typekit kit ships 500/600/700/800 — there is no weight 400.
   Never set font-weight: normal. */
/* hr is in this list because the UA stylesheet gives it margin-inline: auto,
   which centres any decorative rule inside a flex column (§10). */
h1, h2, h3, h4, h5, h6, p, ul, ol, figure, blockquote, hr { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, video { display: block; max-width: 100%; }
img { height: auto; }

/* Backstop against an unbreakable run of text (a long pasted token, URL, or a
   run of non-breaking spaces sanitize.php failed to catch) blowing a layout
   out sideways: break-word only kicks in when the alternative is overflow,
   and min-width: 0 lets a grid item's track shrink below its min-content size
   instead of inflating past the viewport. */
p, li, dt, dd, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
.nhs-grid > * { min-width: 0; }

a {
  color: var(--text-link);
  text-underline-offset: var(--link-underline-offset);
}
/* Buttons are excluded: a bare `a:hover` (0,1,1) outranks `.nhs-btn--secondary`
   (0,1,0), so an anchor-based button lost its label colour on hover and took
   green-700 instead — 1.6:1 on the forest-400 hover fill. Every variant is
   affected, because most buttons in the system are anchors. Each --variant
   already owns its label colour; the base link rule must not reach them.
   Fixed 6 Aug 2026. */
a:not(.nhs-btn):hover { color: var(--text-link-hover); }

/* Icons are sized by their box, never by the path (§19). Box size equals the
   adjacent label's font-size; 12px is the floor. */
.nhs-icon {
  display: block;
  flex: 0 0 auto;
  width: 1em;
  height: 1em;
  fill: currentColor;
}
.nhs-icon--12 { width: 12px; height: 12px; flex-basis: 12px; }
.nhs-icon--16 { width: 16px; height: 16px; flex-basis: 16px; }
.nhs-icon--20 { width: 20px; height: 20px; flex-basis: 20px; }
.nhs-icon--24 { width: 24px; height: 24px; flex-basis: 24px; }
.nhs-icon--32 { width: 32px; height: 32px; flex-basis: 32px; }
.nhs-icon--56 { width: 56px; height: 56px; flex-basis: 56px; }

.nhs-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ------------------------------------------------------------
   §15 Section band — layout shell
   Content sits in a 1200px column inside the outer margin.
   Vertical rhythm: 120 desktop / 96 at 1024 / 64 at 768 / 48 at 375.
   ------------------------------------------------------------ */

.nhs-container {
  width: 100%;
  max-width: calc(var(--content-max) + var(--margin-outer) * 2);
  margin-inline: auto;
  padding-inline: var(--margin-outer);
}

.nhs-band { padding-block: var(--space-13); }
.nhs-band--tint { background: var(--bg-subtle); }
.nhs-band--inverse { background: var(--bg-inverse); color: var(--text-inverse); }
.nhs-band--primary-subtle { background: var(--bg-primary-subtle); }

/* Adjacent bands of the same colour merge their padding rather than
   doubling it (§15). */
.nhs-band--tint + .nhs-band--tint,
.nhs-band--inverse + .nhs-band--inverse,
.nhs-band--primary-subtle + .nhs-band--primary-subtle { padding-block-start: 0; }

@media (max-width: 1023px) { .nhs-band { padding-block: var(--space-12); } }
@media (max-width: 767px)  { .nhs-band { padding-block: var(--space-10); } }
@media (max-width: 480px)  { .nhs-band { padding-block: var(--space-9); } }

/* Fixed-padding modifiers. These sit AFTER the responsive steps deliberately.
   They were declared before them at equal specificity (0,1,0), so every media
   step silently overrode them — .nhs-band--tight rendered 96px below 1024
   instead of 80, i.e. LOOSER than the band it was meant to tighten. Bug found
   and fixed 6 Aug 2026. Keep new fixed-padding modifiers in this block.

   The same-colour merge rule above is (0,2,0) and still wins over these, so
   adjacent bands of one colour continue to merge rather than double. */
/* --tight steps one rung down the base ladder at every breakpoint. Held flat
   at 80 it would be LOOSER than a plain band on mobile (48), which is the
   opposite of what it is for.
     base   120 · 96 · 64 · 48
     tight   80 · 64 · 48 · 32
   --xtight is flat 32: it is already at the floor, and the hero band wants
   the same tight gap at every width. */
.nhs-band--tight  { padding-block: var(--space-11); }  /* 80 */
.nhs-band--xtight { padding-block: var(--space-7); }   /* 32 — hero band */

@media (max-width: 1023px) { .nhs-band--tight { padding-block: var(--space-10); } }  /* 64 */
@media (max-width: 767px)  { .nhs-band--tight { padding-block: var(--space-9);  } }  /* 48 */
@media (max-width: 480px)  { .nhs-band--tight { padding-block: var(--space-7);  } }  /* 32 */

/* Grids used by the card components. Track floors are content-driven —
   see §7 for why the callout floor is 324px. */
.nhs-grid { display: grid; gap: var(--space-6); }
.nhs-grid--2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.nhs-grid--3 { grid-template-columns: repeat(auto-fit, minmax(324px, 1fr)); }
.nhs-grid--4 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }


/* ------------------------------------------------------------
   Buttons — geometry set 03 Aug 2026
   Height tokens are EXACT heights. box-sizing: border-box is set globally
   in nhs-tokens.css so borders and padding never add to them.
   ------------------------------------------------------------ */

.nhs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: 0;
  border-radius: var(--radius-full);
  font-family: inherit;
  font-weight: var(--fw-semibold);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--motion-base) var(--motion-ease),
              color var(--motion-base) var(--motion-ease),
              border-color var(--motion-base) var(--motion-ease);
}

/* Minimum 8px between adjacent buttons. Expressed once, as a gap on the
   group — a sibling margin double-counts against a flex parent's own gap. */
.nhs-btn-group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--btn-gap-min); }

.nhs-btn--lg {
  height: var(--btn-h-lg);            /* 48px */
  padding-inline: var(--btn-px-lg);   /* 32px */
  font-size: var(--fs-button-lg);
  line-height: var(--lh-button-lg);
}
.nhs-btn--md {
  height: var(--btn-h-md);            /* 36px — standard */
  padding-inline: var(--btn-px-md);   /* 20px */
  font-size: var(--fs-button-md);
  line-height: var(--lh-button-md);
}
.nhs-btn--sm {
  height: var(--btn-h-sm);            /* 32px */
  padding-inline: var(--btn-px-sm);   /* 16px */
  font-size: var(--fs-button-sm);
  line-height: var(--lh-button-sm);
}

/* A 36px or 32px button is below the 44px hit-target minimum. Wrap it in
   .nhs-hit to pad the target without changing the painted height. */
.nhs-hit {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
}

/* Fills. Primary, secondary and orange LIGHTEN on hover — that is
   deliberate: every state keeps or improves the label ratio. */
.nhs-btn--primary {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-label);
}
.nhs-btn--primary:hover  { background: var(--btn-primary-bg-hover); }
.nhs-btn--primary:active { background: var(--btn-primary-bg-active); }

.nhs-btn--secondary {
  background: var(--btn-secondary-bg);
  color: var(--btn-secondary-label);
}
.nhs-btn--secondary:hover  { background: var(--btn-secondary-bg-hover); }
.nhs-btn--secondary:active { background: var(--btn-secondary-bg-active); }

.nhs-btn--teal {
  background: var(--btn-teal-bg);
  color: var(--btn-teal-label);
}
.nhs-btn--teal:hover  { background: var(--btn-teal-bg-hover); }
.nhs-btn--teal:active { background: var(--btn-teal-bg-active); }

.nhs-btn--orange {
  background: var(--btn-orange-bg);
  color: var(--btn-orange-label);
}
.nhs-btn--orange:hover  { background: var(--btn-orange-bg-hover); }
.nhs-btn--orange:active { background: var(--btn-orange-bg-active); }

/* Tertiary — outlined. Border must clear 3:1 as a control boundary.

   2px, not 1px — corrected 7 Aug 2026. The approved comp draws every outline
   button at 2px (`border: 2px solid #00341C`); the stylesheet shipped 1px, so
   outlines read visibly thinner than the design. The border colour is forest
   500 for the same reason: the comp uses the text colour, not border-strong.
   Heights are unaffected — box-sizing: border-box is global, so 48/36/32 hold. */
.nhs-btn--tertiary {
  background: transparent;
  color: var(--text-default);
  border: 2px solid var(--text-default);
}
.nhs-btn--tertiary:hover {
  background: var(--bg-subtle);
  border-color: var(--text-default);
}

/* Tertiary primary — the green outline. Present in the approved comp and in
   NHS Foundations, never implemented in the stylesheet until 7 Aug 2026.
   Green-600 border (3.36 on white ✓ as a control boundary) with a green-700
   label (5.83 ✓). Green-500 would fail both roles at 2.05. */
.nhs-btn--tertiary-primary {
  background: transparent;
  color: var(--text-primary-on-light);
  border: 2px solid var(--border-primary);
}
.nhs-btn--tertiary-primary:hover {
  background: var(--bg-primary-subtle);
  border-color: var(--text-primary-on-light);
}

/* Ghost — no boundary, so it may only be used where an adjacent filled
   button establishes the affordance. */
.nhs-btn--ghost {
  background: transparent;
  color: var(--text-default);
  text-decoration: underline;
  text-underline-offset: var(--link-underline-offset);
}
.nhs-btn--ghost:hover { color: var(--text-link-hover); }

.nhs-btn--destructive {
  background: var(--state-error);
  color: var(--nhs-white);
}
/* Derived from the token, not a new hex: CLAUDE.md forbids inventing hex
   values, and state-error is the only crimson in the system. */
.nhs-btn--destructive:hover { background: var(--state-error-hover); }

/* Inverse sets — on a forest surface. */
.nhs-btn--inverse-primary {
  background: var(--nhs-green-500);
  color: var(--nhs-forest-500);
}
.nhs-btn--inverse-primary:hover { background: var(--nhs-green-400); }

.nhs-btn--inverse-outline {
  background: transparent;
  color: var(--nhs-white);
  border: 2px solid var(--nhs-white);
}
.nhs-btn--inverse-outline:hover {
  background: var(--nhs-white);
  color: var(--nhs-forest-500);
}

/* Green pill for use ON TINT — the utility bar's Donate Now, and anywhere a
   primary button sits on the tint band. Green-500 fill, forest label (6.81),
   NO stroke. Designer direction, 6 Aug 2026: the forest border trialled
   earlier the same day is withdrawn.

   KNOWN, ACCEPTED EXCEPTION — WCAG 2.1 1.4.11 Non-text Contrast.
   Green-500 against the tint band is 1.94, so the pill's own boundary does
   not clear the 3:1 minimum. The label is unaffected (forest on green, 6.81
   ✓) and the shape still reads as a button; what fails is the edge, for a
   user who needs the boundary to find the control.

   The one strokeless green that passes is green-700 with a WHITE label —
   boundary 5.53 ✓, label 5.83 ✓ — but it is a much darker olive than the
   brand green in the comp. Swapping is two lines if it is ever wanted.
   Green-600 does not work either way: boundary 3.19 ✓ but no legal 16px
   label (white 3.36, forest 4.15). */
.nhs-btn--primary-on-tint {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-label);
}
.nhs-btn--primary-on-tint:hover  { background: var(--btn-primary-bg-hover); }
.nhs-btn--primary-on-tint:active { background: var(--btn-primary-bg-active); }

/* On an ORANGE surface a white border is 2.92 and fails the 3:1 non-text
   minimum (§13) — the outline goes forest instead. */
.nhs-btn--on-orange-outline {
  background: transparent;
  color: var(--nhs-forest-500);
  border: 2px solid var(--nhs-forest-500);
}
.nhs-btn--on-orange-outline:hover {
  background: var(--nhs-forest-500);
  color: var(--nhs-white);
}

.nhs-btn:disabled,
.nhs-btn[aria-disabled="true"] {
  background: var(--btn-disabled-bg);
  color: var(--btn-disabled-label);
  border-color: transparent;
  cursor: not-allowed;
}

.nhs-btn--block { width: 100%; }

/* At 375 a banner CTA goes full-width (§11). */
@media (max-width: 480px) {
  .nhs-btn--block-sm { width: 100%; }
}

/* Focus ring on a forest surface must be green-300, not forest. */
.nhs-band--inverse :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible,
.nhs-footer :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline-color: var(--focus-ring-inverse);
}


/* ------------------------------------------------------------
   §1 Utility bar
   Tint band, no border — the band gives the bar its own register.
   Donate pill is FOREST with a white label: green on tint is 1.94 and
   fails as a control boundary. Hidden below 768; its links move into
   the drawer and Donate Now promotes to the drawer header.
   ------------------------------------------------------------ */

/* HEADER — restructured 6 Aug 2026 on the designer's direction.

   The utility links no longer sit in their own full-width tint band above the
   nav. Logo goes left; utility row and primary nav stack on the RIGHT, both
   right-aligned, inside one white header.

   The system's hard numbers survive the change exactly: 120px bar, 88px logo,
   and the right-hand stack is two 44px rows — 44 + 44 = 88, the same height
   as the logo, centred in the 120px bar. Mobile steps to 88 / 56 as before.

   Dropdown panels are children of .nhs-header and anchor to it, so they still
   clear the whole bar and nothing below reflows. */
.nhs-header {
  position: relative;              /* positioning context for the panels */
  background: var(--bg-page);
}
.nhs-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-7);
  height: 120px;
}
.nhs-header__stack {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
}
.nhs-header__row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: 44px;
}
.nhs-header__row--utility { gap: var(--space-6); }

@media (max-width: 767px) {
  .nhs-header__inner { height: 88px; }
  .nhs-header__row--utility { display: none; }
}

.nhs-utility__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  font-size: var(--fs-body-md);       /* 16 / 24 */
  line-height: var(--lh-body-md);
  font-weight: var(--fw-medium);      /* Omnes Medium */
  text-decoration: none;
}
.nhs-utility__link:hover { text-decoration: underline; }



/* ------------------------------------------------------------
   §2 Primary navigation
   Bar is a fixed 120px on desktop: 88px logo + 16px above and below.
   Mobile steps the logo to 56px and the bar to 88px. The sticky variant
   holds 56px / 88px on scroll.
   ------------------------------------------------------------ */

/* .nhs-nav and .nhs-nav__inner were the old single-row bar. Superseded by
   .nhs-header / .nhs-header__inner above — removed 6 Aug 2026. The sticky
   variant below still applies, now to .nhs-header. */
.nhs-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

/* Main nav links are 20/28 weight 500. Every item keeps the same 14px
   horizontal padding so no state shifts its siblings. */
.nhs-nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding-inline: 14px;
  border-radius: var(--radius-full);
  font-size: var(--fs-h6);            /* 20 / 28 */
  line-height: var(--lh-h6);
  font-weight: var(--fw-medium);
  color: var(--text-default);
  text-decoration: none;
  background: none;
  border: 0;
  font-family: inherit;
  cursor: pointer;
}

/* Active state is GREEN ONLY — no underline, no bold, no pill. Nav labels
   stay Omnes Medium 500 in every state. Designer direction, 6 Aug 2026,
   reaffirming the original 04 Aug direction; the 3px underline trialled
   earlier the same day is withdrawn.

   KNOWN, ACCEPTED EXCEPTION — WCAG 2.1 1.4.1 Use of Colour (Level A).
   Green-700 alone is a colour-only indicator: against the four closed
   siblings the only difference is hue, so a user who cannot separate green
   from forest cannot tell which page they are on. aria-current="page" means
   screen readers are unaffected; the failure is specific to sighted users
   with colour vision deficiency. The label itself is legible at 5.83 on
   white. A tint pill is the one remaining second cue that does not touch
   weight or add a rule — one line, if it is ever wanted.

   Leaf links never carry a chevron; items with children always do. That
   split is fixed and is what distinguishes two green-700 items in one bar. */
.nhs-nav__link[aria-current="page"],
.nhs-nav__link[aria-expanded="true"] { color: var(--text-primary-on-light); }

.nhs-nav__link:hover { color: var(--text-primary-on-light); }

.nhs-nav__chevron { transition: transform var(--motion-fast) var(--motion-ease); }
.nhs-nav__link[aria-expanded="true"] .nhs-nav__chevron { transform: rotate(180deg); }

/* Dropdown floats over the page: the bar keeps its 120px and nothing below
   reflows. top is calc(100% + 8px) against the BAR, not the trigger —
   anchoring to the trigger makes 100% resolve to the 44px item and the
   panel opens inside the header. */
/* Panel geometry corrected to §2 on 6 Aug 2026 — the shipped CSS disagreed
   with the spec on five values: width 280 → 400, row 44 → 56, radius 12 → 16,
   row padding 12 → 24, and the hover fill tint → green-50. Green-700 on
   green-50 is 5.51 ✓, which is also how the current page reads inside an
   open panel. */
.nhs-nav__panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  z-index: 40;
  width: 400px;
  max-width: calc(100vw - var(--margin-outer) * 2);
  padding: var(--space-5);
  background: var(--nhs-white);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-overlay);
}
.nhs-nav__panel[hidden] { display: none; }
.nhs-nav__panel-list { display: flex; flex-direction: column; }
.nhs-nav__panel-link {
  display: flex;
  align-items: center;
  min-height: 56px;
  padding-inline: var(--space-6);
  border-radius: var(--radius-lg);
  font-size: var(--fs-body-md);
  line-height: var(--lh-body-md);
  font-weight: var(--fw-medium);
  text-decoration: none;
}
.nhs-nav__panel-link:hover,
.nhs-nav__panel-link:focus-visible,
.nhs-nav__panel-link[aria-current="page"] {
  background: var(--bg-primary-subtle);
  color: var(--text-primary-on-light);
}

.nhs-header--sticky { position: sticky; top: 0; z-index: 50; }
.nhs-header--sticky .nhs-header__inner { height: 88px; }
.nhs-header--sticky .nhs-header__row--utility { display: none; }
.nhs-header--sticky .nhs-logo { height: 56px; }

.nhs-nav__toggle { display: none; }

/* Collapse point is 767, matching the utility bar (hidden below 768) and the
   88px mobile bar, so one breakpoint governs the whole header. Between 768
   and 1023 the full nav shows on the 120px bar. If the real link set proves
   too wide at 768, the collapse moves to 1023 — spec question, not a CSS one. */
@media (max-width: 767px) {
  .nhs-nav__list { display: none; }
  .nhs-nav__toggle { display: inline-flex; }
}


/* ------------------------------------------------------------
   §2 Mobile drawer
   ------------------------------------------------------------ */

.nhs-drawer {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding: var(--space-6);
  background: var(--bg-page);
  overflow-y: auto;
}
.nhs-drawer[hidden] { display: none; }
.nhs-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.nhs-drawer__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 2px solid var(--text-default);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--text-default);
  cursor: pointer;
}
.nhs-drawer__list { display: flex; flex-direction: column; }
.nhs-drawer__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 44px;
  font-size: var(--fs-h6);
  line-height: var(--lh-h6);
  font-weight: var(--fw-medium);
  text-decoration: none;
  border-bottom: 1px solid var(--nhs-forest-100);
}
.nhs-drawer__link[aria-current="page"] { color: var(--text-primary-on-light); }


/* ------------------------------------------------------------
   §3 Logo lockup
   88px desktop, 56px mobile. Never below 44px tall. Never on teal or
   orange. Never rotate, outline, shadow, re-set the wordmark or recolour.
   ------------------------------------------------------------ */

.nhs-logo { display: block; height: 88px; width: auto; }
@media (max-width: 767px) { .nhs-logo { height: 56px; } }

/* Clear space equals the cap height of the wordmark on every side. */
.nhs-logo-lockup { display: inline-flex; align-items: center; padding: var(--space-2); }


/* ------------------------------------------------------------
   §4 Footer
   4 columns → 2 at 768 → 1 at 375. Link rows keep 44px throughout.
   ------------------------------------------------------------ */

.nhs-footer {
  background: var(--bg-inverse);
  color: var(--text-inverse);
}
.nhs-footer__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-8);
  padding-block: var(--space-11);
}
.nhs-footer__col { display: flex; flex-direction: column; gap: var(--space-4); }
.nhs-footer__heading {
  font-size: var(--fs-overline);
  line-height: var(--lh-overline);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  color: var(--nhs-green-300);
}
.nhs-footer__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  color: var(--text-inverse);
  text-decoration: none;
}
.nhs-footer__link:hover { color: var(--nhs-green-300); text-decoration: underline; }

/* Social icons are 44×44 targets with accessible names, not bare glyphs. */
.nhs-footer__social { display: flex; gap: var(--space-2); }
.nhs-footer__social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  color: var(--text-inverse);
}
.nhs-footer__social-link:hover { background: var(--nhs-forest-400); }

/* Legal bar is tint at 12px — 13.21 on forest, and the ONLY place
   body-sm is permitted at this contrast. */
.nhs-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  padding-block: var(--space-6);
  border-top: 1px solid var(--nhs-forest-400);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--nhs-tint);
}

@media (max-width: 1023px) { .nhs-footer__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px)  { .nhs-footer__grid { grid-template-columns: 1fr; } }


/* ------------------------------------------------------------
   §5 Newsletter sign-up band
   Submit is FOREST, not green: a green pill on tint is 1.94 and fails
   3:1 as a control boundary. The arrow button needs an accessible name.
   Stacks to one column at 768; input and button stay on one row to 375.
   ------------------------------------------------------------ */

.nhs-newsletter {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: center;
  padding: var(--space-9);
  background: var(--bg-subtle);
  border-radius: var(--radius-xl);
}
.nhs-newsletter__form { display: flex; flex-direction: column; gap: var(--space-3); }
.nhs-newsletter__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-5);
  background: var(--nhs-white);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-full);
}
.nhs-newsletter__input {
  flex: 1 1 auto;
  min-width: 0;
  height: 36px;
  border: 0;
  background: transparent;
  font: inherit;
  color: var(--text-default);
}
.nhs-newsletter__input:focus { outline: none; }
.nhs-newsletter__row:focus-within {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
}
/* GREEN, no stroke — matches .nhs-btn--primary-on-tint, and for the same
   reasons. Designer direction, 6 Aug 2026. The forest arrow reads 6.81 on
   the green fill ✓; the pill's boundary against the tint field is 1.94 and
   is the same accepted 1.4.11 exception recorded on that button.

   ::after extends the hit target to 44×44 without changing the painted 36px
   — the system's 44px floor applies here as everywhere. */
.nhs-newsletter__submit {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 36px;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--nhs-green-500);
  color: var(--nhs-forest-500);
  cursor: pointer;
  transition: background var(--motion-base) var(--motion-ease);
}
.nhs-newsletter__submit::after { content: ""; position: absolute; inset: -4px; }
.nhs-newsletter__submit:hover { background: var(--nhs-green-400); }

@media (max-width: 1023px) { .nhs-newsletter { grid-template-columns: 1fr; } }


/* ------------------------------------------------------------
   §6 Hero panel
   FOREST on green, not white on green — the comp's white H1 was 2.05.
   50/50 at ≥1024; stacks with the image on top at 4:3 below 768 and the
   panel padding drops to 40/24.
   ------------------------------------------------------------ */

/* 50/50 at ≥1024. This is a shared component reused on every page — the split
   is fixed and does not bend to any one page's headline.

   A 3fr 2fr split was trialled on 6 Aug to keep an 84px headline on two lines
   and was WITHDRAWN the same day: the reference hero is 50/50 and its own
   headline runs to three lines, so three is normal at display-xl, not a
   defect. Foundations' "two lines maximum" is guidance for writing headlines,
   not a layout constraint to solve in the grid. Short headlines read best;
   long ones wrap, and that is acceptable. */
.nhs-hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-radius: var(--radius-xl);
  overflow: hidden;
}
/* Horizontal padding is 64, not 96 — reduced 6 Aug 2026 on the designer's
   direction. Kept symmetric: the left inset is what was too deep, but an
   asymmetric panel reads as a mistake, and the right inset is what separates
   the copy from the media edge. Vertical stays 96.

   Side effect worth knowing: content width at 1440 goes 401 → 465, so
   headlines break later. Mobile is unchanged at 40 / 24. */
.nhs-hero__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-12) var(--space-10);
  background: var(--nhs-green-500);
  color: var(--text-on-primary);
}
.nhs-hero__panel--teal {
  background: var(--nhs-teal-600);
  color: var(--nhs-white);
}
/* display-xl 84/88, weight 700 — per the Figma source, confirmed 6 Aug 2026.
   Was display-lg 68/76. §6's responsive note already anticipates display-xl
   here: "the H1 steps to its mobile size (48/56 for display-xl …)".
   Foundations caps display-xl at two lines — see the measure note below. */
.nhs-hero__title {
  font-size: var(--fs-display-xl);
  line-height: var(--lh-display-xl);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-display);
  max-width: 20ch;
  text-wrap: pretty;
}
.nhs-hero__body {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body-lg);
  max-width: 44ch;
  text-wrap: pretty;
}
.nhs-hero__media { position: relative; }
/* Full-bleed hero variant: headline over photography needs the scrim. */
.nhs-hero--bleed { grid-template-columns: 1fr; position: relative; }
.nhs-hero--bleed .nhs-hero__panel { position: relative; background: transparent; color: var(--nhs-white); }
.nhs-hero__media img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 1023px) {
  .nhs-hero { grid-template-columns: 1fr; }
  .nhs-hero__media { order: -1; aspect-ratio: 4 / 3; }
  .nhs-hero__panel { padding: var(--space-8) var(--space-6); }
}


/* ------------------------------------------------------------
   §7 Callout card
   Horizontal: 56px icon left, figure and label stacked right, 20px gap,
   28/32 padding. Figure 40/44 weight 700; label 16/20 weight 600.
   Icons are white at 45% opacity.

   3-up needs a 1308px viewport, not a breakpoint: the card needs 308px to
   hold the 56px icon + 20px gap + 64px padding + the longest 40px figure
   on one line, so the track floor is 324px. Below that it drops to 2-up
   rather than shrinking the figure. 2-up fits at 768 only because the
   outer margin steps to 48px there (768 − 96 = 672 = two 324 + 24 gap).

   CONTRAST: white on orange #FF6633 is 2.92 and FAILS — figure and label
   both. The orange variant is the last failing component in the system and
   is pending the client's choice between orange-600 + white (4.62) and
   #FF6633 + forest (4.77). Do not ship .nhs-callout--orange as-is.
   ------------------------------------------------------------ */

.nhs-callout {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: 28px var(--space-7);
  border-radius: var(--radius-lg);
  color: var(--nhs-white);
}
.nhs-callout__icon { opacity: 0.45; }
.nhs-callout__figure {
  font-size: 2.5rem;                  /* 40 / 44 */
  line-height: 2.75rem;
  font-weight: var(--fw-bold);
}
.nhs-callout__label {
  font-size: var(--fs-stat-label);    /* 16 / 20 */
  line-height: var(--lh-stat-label);
  font-weight: var(--fw-semibold);
}
.nhs-callout--teal { background: var(--nhs-teal-600); }   /* 5.06 ✓ */
.nhs-callout--orange { background: var(--nhs-orange-500); } /* 2.92 ✗ — see above */


/* ------------------------------------------------------------
   §7 Stat tile
   Teal-500 is WITHDRAWN from the stat tile: forest on teal-500 is 4.16,
   legal for the 64px figure but illegal for the 16px label. teal-600 with
   a white figure and label (5.06) is the only teal tile. The CMS should
   offer one teal option.
   ------------------------------------------------------------ */

.nhs-stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-7);
  border-radius: var(--radius-lg);
}
.nhs-stat__figure {
  font-size: var(--fs-stat-figure);
  line-height: var(--lh-stat-figure);
  font-weight: var(--fw-bold);
}
.nhs-stat__label {
  font-size: var(--fs-stat-label);
  line-height: var(--lh-stat-label);
  font-weight: var(--fw-semibold);
}
.nhs-stat--tint  { background: var(--bg-subtle); color: var(--text-default); }
.nhs-stat--green { background: var(--nhs-green-500); color: var(--text-on-primary); }
.nhs-stat--teal  { background: var(--nhs-teal-600); color: var(--nhs-white); }
.nhs-stat--forest{ background: var(--bg-inverse); color: var(--text-inverse); }


/* ------------------------------------------------------------
   §8 Animal card
   4-up ≥1024 · 2-up 768 · 1-up 375. Photos are always 4:3 and never crop
   the animal's eyes. Every status badge carries an icon AND a text label —
   never colour alone.
   ------------------------------------------------------------ */

.nhs-card {
  display: flex;
  flex-direction: column;
  background: var(--nhs-white);
  border: 1px solid var(--nhs-forest-100);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.nhs-card__media { position: relative; aspect-ratio: 4 / 3; }
.nhs-card__media img { width: 100%; height: 100%; object-fit: cover; }
.nhs-card__flag { position: absolute; top: var(--space-3); left: var(--space-3); }
.nhs-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
}
.nhs-card__title {
  font-size: var(--fs-h5);
  line-height: var(--lh-h5);
  font-weight: var(--fw-bold);
}
.nhs-card__meta {
  font-size: var(--fs-body-md);
  line-height: var(--lh-body-md);
  color: var(--text-muted);
}
.nhs-card__foot { margin-top: auto; padding: 0 var(--space-5) var(--space-5); }

/* Skeleton is aria-hidden; the list is aria-busy="true" while loading. */
.nhs-card--skeleton .nhs-card__media,
.nhs-card--skeleton .nhs-card__line { background: var(--nhs-forest-100); }
.nhs-card__line { height: 16px; border-radius: var(--radius-sm); }


/* ------------------------------------------------------------
   §9 Section intro block
   FOREST heading, centred, measure capped at 60ch. Green headings are
   only legal on a forest surface (§10).
   ------------------------------------------------------------ */

.nhs-intro {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  align-items: center;
  text-align: center;
  max-width: 60ch;
  margin-inline: auto;
}
.nhs-intro__title {
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-heading);
  color: var(--text-default);
  text-wrap: pretty;
}
.nhs-intro__body {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body-lg);
  color: var(--text-muted);
  text-wrap: pretty;
}


/* ------------------------------------------------------------
   §10 Section header with rule
   The 3px rule keeps green — green-600 clears 3:1 (3.36) as a non-text
   element. The heading itself is forest.
   ------------------------------------------------------------ */

.nhs-section-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.nhs-section-header__title {
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-heading);
}
.nhs-section-header__rule {
  width: var(--space-10);
  height: 3px;
  margin: 0;
  background: var(--border-primary);
  border: 0;
}
/* The only sanctioned green-family heading treatment: reversed onto forest. */
.nhs-section-header--inverse .nhs-section-header__title { color: var(--nhs-green-300); }


/* ------------------------------------------------------------
   §11 Full-width CTA banner
   Green banner keeps its forest headline (6.81). The teal variant steps to
   teal-600 so a 28px white headline and a white pill both clear AA.
   Stacks left-aligned below 768; button full-width at 375.
   ------------------------------------------------------------ */

.nhs-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
  padding: var(--space-9) var(--space-10);
  border-radius: var(--radius-xl);
  background: var(--nhs-green-500);
  color: var(--text-on-primary);
}
.nhs-cta--teal { background: var(--nhs-teal-600); color: var(--nhs-white); }
.nhs-cta--forest { background: var(--bg-inverse); color: var(--text-inverse); }
.nhs-cta__title {
  font-size: var(--fs-h4);
  line-height: var(--lh-h4);
  font-weight: var(--fw-bold);
  max-width: 40ch;
  text-wrap: pretty;
}

@media (max-width: 767px) {
  .nhs-cta { flex-direction: column; align-items: flex-start; }
}


/* ------------------------------------------------------------
   §12 Campaign feature block
   Overlaid white headline requires the forest scrim at ≥72%. Copy side
   sits on tint so the block reads as one unit; body measure 56ch.
   Image on top at 16:9 below 1024, scrim unchanged.
   ------------------------------------------------------------ */

.nhs-campaign {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.nhs-campaign__media { position: relative; min-height: 420px; }
/* Overlaid white headline sits on the scrim, not on the raw photo. */
.nhs-campaign__overlay {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  padding: var(--space-8);
  color: var(--nhs-white);
}
.nhs-campaign__media img { width: 100%; height: 100%; object-fit: cover; }
.nhs-campaign__copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-10);
  background: var(--bg-subtle);
}
.nhs-campaign__body { max-width: 56ch; text-wrap: pretty; }

@media (max-width: 1023px) {
  .nhs-campaign { grid-template-columns: 1fr; }
  .nhs-campaign__media { order: -1; aspect-ratio: 16 / 9; min-height: 0; }
  .nhs-campaign__copy { padding: var(--space-8) var(--space-6); }
}


/* ------------------------------------------------------------
   §13 Action card
   Orange cards are FOREST text (4.77), not white (2.92). Teal cards step
   to teal-600 so 16px white body reads 5.06. On orange the outline button
   takes a forest border and label — white is 2.92 against the fill.
   4-up ≥1024 · 2-up 768 · 1-up 375. Cards stretch to equal height so the
   buttons align.
   ------------------------------------------------------------ */

.nhs-action {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  align-items: flex-start;
  height: 100%;
  padding: var(--space-7);
  border-radius: var(--radius-lg);
}
/* Media — added 6 Aug 2026. §13 always specified a 4:3 candid image on the
   action card and the stylesheet never implemented it; cards were text-only.

   The card keeps its 32px padding, and the media breaks out of it with a
   negative margin so the image sits flush to the card's top and sides. Done
   this way deliberately: moving the padding to an inner __body would have
   required a markup change on every action card that has no image, and a
   card without media must keep working untouched. */
.nhs-action__media {
  position: relative;
  width: calc(100% + var(--space-7) * 2);
  margin: calc(var(--space-7) * -1) calc(var(--space-7) * -1) var(--space-2);
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  overflow: hidden;
}
.nhs-action__media img { width: 100%; height: 100%; object-fit: cover; }

.nhs-action__title {
  font-size: var(--fs-h4);
  line-height: var(--lh-h4);
  font-weight: var(--fw-bold);
}
.nhs-action__body { flex: 1 1 auto; text-wrap: pretty; }
.nhs-action--green  { background: var(--nhs-green-500); color: var(--text-on-primary); }
.nhs-action--teal   { background: var(--nhs-teal-600);  color: var(--nhs-white); }
.nhs-action--orange { background: var(--nhs-orange-500); color: var(--text-on-accent-orange); }
.nhs-action--tint   { background: var(--bg-subtle); color: var(--text-default); }
/* Surface — added 7 Aug 2026 for the Adoption Process page. §13 shipped four
   coloured fills and no neutral one, so an action card standing on a TINT
   band had nowhere to go: --tint on --tint is invisible and the three accent
   fills assign a meaning the content does not carry.

   Border is --border-default (forest-300), NOT .nhs-card's forest-100. The
   fill cannot supply the boundary here — white on the tint band measures
   1.05 — so the hairline is the only thing separating the card from its
   ground and has to clear the 3:1 non-text minimum by itself. Measured:
   forest-300 is 3.77 vs tint and 3.97 vs white, so one border serves both
   grounds. forest-100 was written first, copied from .nhs-card, and measures
   1.27 vs tint. See the changelog — .nhs-card carries the same weakness. */
.nhs-action--surface {
  background: var(--nhs-white);
  color: var(--text-default);
  border: 1px solid var(--border-default);
}


/* ------------------------------------------------------------
   §13b Process step — added 7 Aug 2026
   A numbered stage in a sequence: number token, then title and body. Built
   for the Adoption Process page's eight steps and expected to recur on
   Volunteer, Foster and Donate.

   Horizontal, not a stacked .nhs-action: the number is an ordinal, so it
   reads beside its label rather than above it, and a 2-up grid of eight
   vertical cards would run the section past three screens.

   Always inside an <ol>, never a <ul> — the sequence belongs in the markup.
   The number token is decorative reinforcement of an order the list already
   carries, which is why it may safely be text.
   ------------------------------------------------------------ */

/* The step list needs its own track floor, not .nhs-grid--2.
   .nhs-grid--2 is auto-fit minmax(280px, 1fr) — it is a MINIMUM TRACK, not a
   column count, and it reads as "2-up" only because every existing use has
   exactly two children. Eight steps in it pack 3-up at 1440 and leave a
   ragged final row of two.

   420px is content-driven, the same reasoning as the callout's 324px floor:
   a step is a 48px number + 20px gap + 48px padding + a title that must not
   wrap to three lines. At 420 the grid holds 2-up from 1024 up and drops to
   1-up below, which is what the wireframe specifies.
   min() lets the track collapse under 420 rather than overflow at 375. */
.nhs-steps {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
}

.nhs-step {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
  height: 100%;
  padding: var(--space-6);
  background: var(--nhs-white);
  /* Same reasoning as .nhs-action--surface: the step sits on the tint band,
     white on tint is 1.05, so the hairline is the boundary. forest-300 =
     3.77 vs tint, 3.97 vs white. */
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
}
/* The copy column carries the measure cap itself rather than relying on the
   page to remember .nhs-measure. Below 1024 the step grid drops to 1-up, and
   an uncapped step body measured 86 characters at a 900px viewport — past the
   72ch the rest of the system holds to. The cap belongs to the component
   because the overrun is caused by the component's own responsive behaviour. */
.nhs-step__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: var(--measure-max);
}

/* 48px = --space-9, the large-button height, so a step number and a button
   read at the same scale. Forest fill carries a white numeral at 13.93. */
.nhs-step__number {
  flex: 0 0 var(--space-9);
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-9);
  height: var(--space-9);
  border-radius: var(--radius-full);
  background: var(--bg-inverse);
  color: var(--text-inverse);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body-lg);
  font-weight: var(--fw-bold);
}


/* ------------------------------------------------------------
   §14 Image feature panel
   Forest at 72% opacity MINIMUM over any photograph, applied as a
   full-panel flood, not a gradient. That is where white 16px body copy
   clears 4.5:1 against the worst-case bright patch of a daylight photo.
   A 40–100% vertical gradient is permitted only if the text sits inside
   the ≥72% band and the CMS crop enforces the safe area.
   ------------------------------------------------------------ */

.nhs-panel {
  position: relative;
  display: flex;
  align-items: flex-end;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-xl);
  overflow: hidden;
  color: var(--nhs-white);
}
.nhs-panel img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Text sits in the lower 60% with 48px clear of every edge, and never over
   the subject's face. */
.nhs-panel__copy {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  align-items: flex-start;
  max-width: 56ch;
  padding: var(--space-9);
}


/* ------------------------------------------------------------
   Scrim — §6 hero, §12 campaign, §14 image panel
   Forest at 72% is the MINIMUM that holds white 16px body copy at 4.5:1
   over the worst-case bright patch of a daylight photograph. Applied as a
   full flood, not a gradient. One class, because one number: three
   components share it and it must never drift between them.
   ------------------------------------------------------------ */

.nhs-scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim-forest);
}
/* A vertical gradient is permitted ONLY if the text sits inside the ≥72%
   band and the CMS crop enforces the safe area (§14). */
.nhs-scrim--gradient {
  background: linear-gradient(to top,
    var(--scrim-forest) 0%,
    var(--scrim-forest) 60%,
    rgb(from var(--nhs-forest-500) r g b / 0.40) 100%);
}

/* ------------------------------------------------------------
   §17 Links
   Forest, underlined, 4px offset. Hover → green-700, underline retained.
   On forest: green-300 (9.30). NEVER green-500 on forest for link text.
   ------------------------------------------------------------ */

.nhs-link {
  color: var(--text-link);
  text-decoration: underline;
  text-underline-offset: var(--link-underline-offset);
}
.nhs-link:hover { color: var(--text-link-hover); }

.nhs-link--inverse { color: var(--nhs-green-300); }
.nhs-link--inverse:hover { color: var(--nhs-green-200); }

/* On a GREEN surface — the hero panel and the green action card. The link
   keeps forest (6.81) and gains its underline on hover, because the standard
   green-700 hover is 2.85 on green-500 and fails. There is no green step that
   works as link text on green: 700 is 2.85, 800 is 1.35. Colour cannot carry
   the hover here, so the underline does. Added 6 Aug 2026. */
.nhs-link--on-primary,
.nhs-link--on-primary:hover { color: var(--text-on-primary); }
.nhs-link--on-primary:hover { text-decoration: underline; }

/* Standalone link with an arrow — 44px target, no underline until hover. */
.nhs-link--standalone {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body-lg);
  font-weight: var(--fw-semibold);
  text-decoration: none;
}
.nhs-link--standalone:hover { color: var(--text-link-hover); text-decoration: underline; }


/* ------------------------------------------------------------
   §18 Form elements
   Inputs are 48px so they match a large button in a row. Validation
   messages take a 20px icon beside 16px text — a deliberate departure
   from the box=font-size rule, because an error indicator that small is
   easy to miss (§19).
   ------------------------------------------------------------ */

.nhs-field { display: flex; flex-direction: column; gap: var(--space-2); }
.nhs-field__label {
  font-size: var(--fs-body-md);
  line-height: var(--lh-body-md);
  font-weight: var(--fw-semibold);
}
.nhs-field__hint {
  font-size: var(--fs-body-md);
  line-height: var(--lh-body-md);
  color: var(--text-muted);
}

.nhs-input,
.nhs-select {
  width: 100%;
  /* Exact 48px so an input and a large button align in a row. Vertical
     padding is 0 — the height token owns vertical size, same rule as the
     buttons. Padding + line-height + borders would otherwise total 50. */
  height: var(--btn-h-lg);
  padding: 0 var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--nhs-white);
  font: inherit;
  color: var(--text-default);
}
.nhs-textarea {
  width: 100%;
  min-height: 120px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--nhs-white);
  font: inherit;
  color: var(--text-default);
  resize: vertical;
}
.nhs-input::placeholder,
.nhs-textarea::placeholder { color: var(--nhs-forest-300); }

.nhs-input[aria-invalid="true"],
.nhs-select[aria-invalid="true"],
.nhs-textarea[aria-invalid="true"] { border-color: var(--state-error); border-width: 2px; }

.nhs-input:disabled,
.nhs-select:disabled,
.nhs-textarea:disabled {
  background: var(--nhs-forest-50);
  border-color: var(--nhs-forest-200);
  color: var(--nhs-forest-300);
  cursor: not-allowed;
}

/* Checkbox and radio: 24px control, 44px row. */
.nhs-choice {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  cursor: pointer;
}
.nhs-choice__control {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  margin: 0;
  accent-color: var(--nhs-forest-500);
}

.nhs-validation {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-body-md);
  line-height: var(--lh-body-md);
  font-weight: var(--fw-semibold);
}
.nhs-validation--error   { color: var(--state-error); }
.nhs-validation--success { color: var(--state-success); }
.nhs-validation--warning { color: var(--state-warning); }


/* ------------------------------------------------------------
   §20 Badge / tag
   Attribute tags use the 100–200 ramp steps so a row of them never
   competes with a CTA. Status badges — the ones that change what a user
   can do — use the 500 steps PLUS an icon. 12px at weight 600 is the one
   place body-sm-sized type is permitted, because every fill here carries
   forest at 9.62 or better.
   ------------------------------------------------------------ */

.nhs-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px var(--space-3);
  border-radius: var(--radius-full);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
}
/* Attribute tags — no icon. */
.nhs-badge--attr-green  { background: var(--nhs-green-100); color: var(--text-default); }
.nhs-badge--attr-teal   { background: var(--nhs-teal-100);  color: var(--text-default); }
.nhs-badge--attr-orange { background: var(--nhs-orange-200); color: var(--text-default); }
/* Status badges — icon required. */
.nhs-badge--new     { background: var(--nhs-green-500);  color: var(--text-on-primary); }
.nhs-badge--urgent  { background: var(--nhs-orange-500); color: var(--text-on-accent-orange); }
.nhs-badge--adopted { background: var(--nhs-forest-500); color: var(--text-inverse); }


/* ------------------------------------------------------------
   §21a Breadcrumb
   ------------------------------------------------------------ */

.nhs-breadcrumb__list { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.nhs-breadcrumb__item { display: flex; align-items: center; gap: var(--space-2); }
.nhs-breadcrumb__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-body-md);
  line-height: var(--lh-body-md);
}
.nhs-breadcrumb__sep { color: var(--nhs-forest-300); }
.nhs-breadcrumb__item[aria-current="page"] { color: var(--text-muted); }


/* ------------------------------------------------------------
   §21b Pagination
   Current page is a forest FILL, not just bold. Disabled Prev is not
   focusable. Every target is 44×44. Page changes announce
   "Showing 13–24 of 96 animals" in a polite live region.

   Prev/Next use text arrows because arrow-left was never supplied (§19).
   ------------------------------------------------------------ */

.nhs-pagination { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.nhs-pagination__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding-inline: var(--space-4);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-full);
  font-size: var(--fs-body-md);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  color: var(--text-default);
}
.nhs-pagination__item:hover { border-color: var(--text-default); background: var(--bg-subtle); }
.nhs-pagination__item[aria-current="page"] {
  background: var(--nhs-forest-500);
  border-color: var(--nhs-forest-500);
  color: var(--text-inverse);
}
.nhs-pagination__item[aria-disabled="true"] {
  color: var(--nhs-forest-300);
  border-color: var(--nhs-forest-100);
  pointer-events: none;
}


/* ------------------------------------------------------------
   §21c Accordion
   ------------------------------------------------------------ */

.nhs-accordion { display: flex; flex-direction: column; }
.nhs-accordion__item { border-bottom: 1px solid var(--nhs-forest-100); }
.nhs-accordion__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  width: 100%;
  min-height: 44px;
  padding: var(--space-4) 0;
  border: 0;
  background: transparent;
  font-family: inherit;
  font-size: var(--fs-h6);
  line-height: var(--lh-h6);
  font-weight: var(--fw-semibold);
  color: var(--text-default);
  text-align: left;
  cursor: pointer;
}
.nhs-accordion__trigger:hover { color: var(--text-primary-on-light); }
.nhs-accordion__icon { transition: transform var(--motion-fast) var(--motion-ease); }
.nhs-accordion__trigger[aria-expanded="true"] .nhs-accordion__icon { transform: rotate(180deg); }
.nhs-accordion__panel { padding-block: 0 var(--space-5); max-width: var(--measure-max); }
.nhs-accordion__panel[hidden] { display: none; }


/* ------------------------------------------------------------
   §21d Tabs
   Selected tab carries a 3px green-600 underline AND weight 700 — the
   selected state is never colour-only. Arrow keys move between tabs, Tab
   moves into the panel. Below 768 tabs become an accordion rather than
   scrolling horizontally.
   ------------------------------------------------------------ */

.nhs-tabs__list {
  display: flex;
  gap: var(--space-6);
  border-bottom: 1px solid var(--nhs-forest-100);
}
.nhs-tabs__tab {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-1);
  border: 0;
  border-bottom: 3px solid transparent;
  background: transparent;
  font-family: inherit;
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body-lg);
  font-weight: var(--fw-medium);
  color: var(--text-muted);
  cursor: pointer;
}
.nhs-tabs__tab[aria-selected="true"] {
  border-bottom-color: var(--border-primary);
  font-weight: var(--fw-bold);
  color: var(--text-default);
}
.nhs-tabs__panel { padding-block-start: var(--space-6); }
.nhs-tabs__panel[hidden] { display: none; }

@media (max-width: 767px) {
  .nhs-tabs--to-accordion .nhs-tabs__list { display: none; }
}


/* ------------------------------------------------------------
   Typography utilities
   Green headings are legal ONLY on a forest surface. Tier 3 colours are
   never used for text, borders or icons.
   ------------------------------------------------------------ */

.nhs-display-xl { font-size: var(--fs-display-xl); line-height: var(--lh-display-xl); font-weight: var(--fw-bold); letter-spacing: var(--ls-display); }
.nhs-display-lg { font-size: var(--fs-display-lg); line-height: var(--lh-display-lg); font-weight: var(--fw-bold); letter-spacing: var(--ls-display); }
.nhs-h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: var(--fw-bold); letter-spacing: var(--ls-heading); }
.nhs-h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); font-weight: var(--fw-bold); letter-spacing: var(--ls-heading); }
.nhs-h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); font-weight: var(--fw-bold); letter-spacing: var(--ls-heading); }
.nhs-h4 { font-size: var(--fs-h4); line-height: var(--lh-h4); font-weight: var(--fw-bold); }
.nhs-h5 { font-size: var(--fs-h5); line-height: var(--lh-h5); font-weight: var(--fw-semibold); }
.nhs-h6 { font-size: var(--fs-h6); line-height: var(--lh-h6); font-weight: var(--fw-semibold); }
.nhs-body-lg { font-size: var(--fs-body-lg); line-height: var(--lh-body-lg); }
.nhs-body-md { font-size: var(--fs-body-md); line-height: var(--lh-body-md); }
.nhs-overline {
  font-size: var(--fs-overline);
  line-height: var(--lh-overline);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
}
.nhs-measure { max-width: var(--measure-max); text-wrap: pretty; }
.nhs-muted { color: var(--text-muted); }

/* List — added 7 Aug 2026. The base reset at the top of this file strips
   list-style and padding from every ul/ol, which is right for nav, footer
   and card grids (all of them lists) but leaves prose lists unmarked and
   flush, so items run together. This opts a list back in.

   Marker is forest-400 (--text-muted), the same relationship a bullet has
   to its text elsewhere in the system: present, subordinate to the copy.
   Row gap is space-3 rather than a margin — sibling spacing goes through
   gap throughout.

   Wired up 31 Aug 2026: sanitize.php stamps this class onto every ul/ol a
   rich-text field saves (Aug 31 meeting: "text editor does not support
   bullets"), which is why this had sat unused since the 7 Aug commit. List
   type (bullet vs numbered) still needs the real tag, so list-style is
   split per tag rather than fixed on the shared class. */
.nhs-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-inline-start: var(--space-5);
}
ul.nhs-list { list-style: disc; }
ol.nhs-list { list-style: decimal; }
.nhs-list > li { text-wrap: pretty; }
.nhs-list > li::marker { color: var(--text-muted); }

/* Stack utility — sibling spacing goes through gap, never margins. */
.nhs-stack { display: flex; flex-direction: column; }
.nhs-stack--2 { gap: var(--space-2); }
.nhs-stack--4 { gap: var(--space-4); }
.nhs-stack--6 { gap: var(--space-6); }
.nhs-stack--8 { gap: var(--space-8); }
.nhs-row { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }


/* ------------------------------------------------------------
   §4 + §5 Approved footer treatment — added 6 August 2026

   The approved footer carries the newsletter ON the forest ground rather
   than as a separate tint band, with a hairline rule above and below it,
   and a legal bar split left/right. These live here, not in a page, so the
   pattern has one home.

   MIRROR THIS INTO NHS Components.dc.html §4 AND §5 — a number, and a
   treatment, lives in one place conceptually.

   Contrast, all against forest-500:
     green-500 heading   6.81  ✓ (green headings are legal on forest)
     green-300 link      9.30  ✓ (§17: never green-500 for link text)
     tint 12px consent  13.21  ✓ (same allowance as the legal bar)
   The submit pill and field border stay forest — see the note below.
   ------------------------------------------------------------ */

.nhs-footer__rule {
  margin: 0;
  border: 0;
  height: 1px;
  background: var(--nhs-forest-400);
}

/* Links are green-300 on the forest ground, not white. */
.nhs-footer__link { color: var(--nhs-green-300); }
.nhs-footer__link:hover { color: var(--nhs-green-200); text-decoration: underline; }

/* Masthead column (logo) and the CTA column (button + social). */
.nhs-footer__brand,
.nhs-footer__cta {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  align-items: flex-start;
}
.nhs-footer__logo { display: block; height: 88px; width: auto; }

.nhs-footer__legal--split { justify-content: space-between; align-items: center; }

/* §5 Newsletter, inverse — sits on the footer's forest ground. The field
   keeps a tint surface so the typed value stays forest-on-tint. */
.nhs-newsletter--inverse {
  padding: var(--space-9) 0;
  background: transparent;
  border-radius: 0;
  align-items: start;
}
.nhs-newsletter--inverse .nhs-newsletter__row { background: var(--bg-subtle); }

/* The submit pill and the field border stay FOREST here, exactly as in the
   base component. The comp draws both in green; §5 records the correction and
   the changelog states it plainly — "Utility bar and newsletter submit. Both
   were green on tint in the comp (1.94) and are now forest." Green on tint is
   1.94 and fails the 3:1 control-boundary minimum, and green-300 on tint is
   worse. Do not re-green either one without a route that clears 3:1. */

.nhs-newsletter__title {
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-heading);
  color: var(--nhs-green-500);
  max-width: 24ch;
  text-wrap: pretty;
}
.nhs-newsletter__consent {
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--nhs-tint);
  max-width: 52ch;
}
.nhs-newsletter__consent a { color: var(--nhs-green-300); font-weight: var(--fw-bold); }

@media (max-width: 480px) {
  .nhs-footer__legal--split { flex-direction: column; align-items: flex-start; }
}

/* Rhythm corrections so §4 matches the approved comp and the validation
   assembly in NHS Homepage.dc.html, which both run tighter than the shipped
   16px column gap: link rows sit on a 48px rhythm (4px gap + 44px target),
   with 8px under the group heading. */
.nhs-footer { padding-block-start: var(--space-11); }
.nhs-footer__col { gap: var(--space-1); }
.nhs-footer__heading { margin-bottom: var(--space-2); }
.nhs-footer__social-link { text-decoration: none; font-weight: var(--fw-bold); }

/* ------------------------------------------------------------
   Type-weight corrections — 6 August 2026

   Three weights in this stylesheet contradict the spec. The brief's weight
   table, the type scale, tokens.json and NHS Foundations.dc.html all agree
   with each other and disagree with the CSS, so per the handoff rule the
   spec wins and the stylesheet is the bug.

     .nhs-h4          was 700 → 600   (h4 · 28/36 · 600; "600 SemiBold: H4–H6")
     .nhs-stat__label was 600 → 500   (stat-label · 16/20 · 500)

   NOT changed: .nhs-btn stays at 600 SemiBold, as originally shipped.
   tokens.json and the brief both list button-lg/md/sm at 700, but the Figma
   source sets button text in Omnes SemiBold and the designer confirmed it on
   6 Aug. Where the token file and the design file disagree on a visible
   design property, the design file wins — leave this alone.

   MIRROR INTO NHS Components.dc.html. Reverting is one line each.
   ------------------------------------------------------------ */
.nhs-h4 { font-weight: var(--fw-semibold); }
.nhs-stat__label { font-weight: var(--fw-medium); }
