/* SlyFox Solutions — Design System
   Generated by Frontend Wireframe Engine, Phase 4
   DNA Hash: e1f322961756
   Do not edit by hand. Regenerate via the engine.

   Axes: humanist-warm-readable / editorial-understated / editorial-magazine /
         offset-editorial / underline-reveal / photography-editorial /
         subtle-elegant / airy / off-white-sophisticated / solid-minimal /
         topographic-map / cultural: none
   Mutations: grid-break (spatial), topographic-pattern (textural), accent-font (typographic)
*/

:root {
  /* ── Typography ──────────────────────────────────────────────
     Single family: Raleway, per client direction. The three roles are now
     separated by weight, size, and tracking rather than by typeface.
     One family is comfortably inside the Font Buffet ceiling of three. */
  --font-display: 'Raleway', system-ui, sans-serif;
  --font-body: 'Raleway', system-ui, sans-serif;
  --font-data: 'Raleway', system-ui, sans-serif;

  --scale-ratio: 1.333;
  --display-weight: 800;
  --heading-weight: 700;
  --body-weight: 400;
  --display-tracking: -0.03em;   /* floor is -0.04em; we stay above it */
  --heading-tracking: -0.02em;
  --data-tracking: 0.13em;
  --display-leading: 1.02;
  --heading-leading: 1.15;
  --body-leading: 1.72;
  --body-size: 17.5px;
  --measure: 68ch;
  --measure-tight: 46ch;

  /* Fluid display scale, brand register */
  --step-hero: clamp(2.6rem, 1.15rem + 5.6vw, 5.5rem);
  --step-1: clamp(2rem, 1.15rem + 3.4vw, 3.75rem);
  --step-2: clamp(1.55rem, 1.1rem + 1.8vw, 2.5rem);
  --step-3: clamp(1.25rem, 1.05rem + 0.85vw, 1.65rem);
  --step-4: 1.125rem;

  /* ── Colour ──────────────────────────────────────────────────
     Every ratio below was measured, not estimated.
     The ground is cool, carrying a trace of the fox tail's navy.
     It is deliberately NOT cream: that band is the 2026 AI tell. */
  --paper:        #f6f7f9;   /* base surface */
  --surface:      #eceef3;   /* second surface, alternating bands */
  --navy:         #1e2a5a;   /* fox tail navy, contrast bands */
  --navy-deep:    #151b2e;   /* ink */
  --ink:          #151b2e;   /* 15.95:1 on paper */
  --slate:        #4a5468;   /*  7.10:1 on paper, secondary body */
  --steel:        #3f6e97;   /*  5.04:1 on paper, tertiary and metadata */
  --mist:         #b9c4da;   /*  7.80:1 on navy, body inside dark bands */
  --line:         rgba(21, 27, 46, 0.14);
  --line-strong:  rgba(21, 27, 46, 0.30);
  --line-invert:  rgba(246, 247, 249, 0.22);
  --abyss:        #151b2e;   /* deepest band. fox orange reaches 5.21:1 here */
  --abyss-line:   rgba(246, 247, 249, 0.16);

  /* The single locked accent. Fox orange, straight off the logo. */
  --fox:        #f0601e;   /* 3.06:1 on paper. LARGE type, rules, fills ONLY. */
  --fox-amber:  #f0901a;   /* 5.68:1 on navy. Small accent text on dark only. */
  --rust:       #b8410f;   /* 5.15:1 on paper. Any orange below 24px. */

  /* ── Spacing, from the airy philosophy ─────────────────────── */
  --s-1: 8px;   --s-2: 16px;  --s-3: 24px;  --s-4: 32px;
  --s-5: 48px;  --s-6: 64px;  --s-7: 80px;  --s-8: 96px;  --s-9: 128px;
  --section-gap: 96px;
  --pad-x: 24px;
  --maxw: 1440px;

  /* ── Motion, subtle-elegant over the craft floor ───────────── */
  --foxfield-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='290' height='290' viewBox='0 0 290 290'%3E%3Cg fill='%231e2a5a'%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.11' transform='translate(22,20)'/%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.08' transform='translate(196,146) rotate(-9) scale(0.62)'/%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.095' transform='translate(96,176) rotate(11) scale(0.42)'/%3E%3C/g%3E%3C/svg%3E");
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
  --enter-duration: 500ms;
  --stagger: 60ms;
  --control-duration: 160ms;   /* controls stay on the fast floor */
  --hover-duration: 200ms;
}

@media (min-width: 768px)  { :root { --pad-x: 48px; } }
@media (min-width: 1280px) { :root { --pad-x: 80px; --section-gap: 128px; } }

/* ══ Base ══════════════════════════════════════════════════════ */
* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background-color: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--body-size);
  font-weight: var(--body-weight);
  line-height: var(--body-leading);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: var(--heading-weight);
  letter-spacing: var(--heading-tracking);
  line-height: var(--heading-leading);
  margin: 0;
  text-wrap: balance;
}

p { margin: 0 0 var(--s-3); max-width: var(--measure); }

a { color: inherit; }

img { max-width: 100%; display: block; }

::selection { background: var(--fox); color: var(--navy-deep); }

:focus-visible {
  outline: 2px solid var(--fox);
  outline-offset: 3px;
}

/* ══ Layout primitives ═════════════════════════════════════════ */
.shell {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}

/* editorial-magazine: a real 12-column grid so no two sections
   have to share a composition. */
.grid12 {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--s-4);
}
/* Gaps do not shrink. Eleven 32px gaps are 352px of fixed width, so with the
   48px shell padding this grid had a hard minimum of 400px and overflowed on
   every viewport below that: 56px at 320px, and 1px at 375px, which is the
   "rounding artifact" that kept showing up in audits. Columns stack below the
   lg breakpoint anyway, so a narrower column gap costs nothing visually. */
@media (max-width: 1023px) {
  .grid12 { column-gap: 16px; }
}

.section { padding-block: var(--section-gap); }
.section--tight { padding-block: var(--s-6); }
.section--surface { background-color: var(--surface); }

/* Inner-page head. Without this, a full section-gap below the headline plus a
   full section-gap above the next block stacks to roughly 256px of dead air. */
.section--pagehead { padding-block: var(--s-7) var(--s-5); }
.section--pagehead + .section,
.section--pagehead + .section--tight { padding-top: var(--s-6); }

/* Contrast bands. Prevent the page reading as one continuous document.
   band-abyss is the deepest: at #151b2e the fox orange reaches 5.21:1 and
   becomes legal for small text, which it is not on the lighter navy. */
.band-navy  { background-color: var(--navy);  color: var(--paper); }
.band-abyss { background-color: var(--abyss); color: var(--paper); }

/* ── Dark surface contract ────────────────────────────────────
   EVERY dark context is listed here, once: .band-navy, .band-abyss,
   .hero--dark, .footer. This has now been missed FOUR times,
   each time shipping something broken: steel #3f6e97 measures 2.53:1 on navy,
   and a navy .btn on a navy surface measures 1.00:1 and is invisible.
   Any new dark surface MUST be added to these selectors rather than given its
   own overrides, and must be checked for BOTH text colour and button fill. */
.band-navy h1, .band-navy h2, .band-navy h3, .band-navy h4,
.band-abyss h1, .band-abyss h2, .band-abyss h3, .band-abyss h4,
.hero--dark h1, .hero--dark h2, .hero--dark h3, .hero--dark h4,
.footer h1, .footer h2, .footer h3, .footer h4 { color: var(--paper); }

.band-navy p, .band-abyss p, .hero--dark p, .footer p,
.band-navy .lede, .band-abyss .lede,
.band-navy .muted, .band-abyss .muted, .hero--dark .muted, .footer .muted { color: var(--mist); }

/* The data layer switches to amber on navy (5.68:1) and to full fox orange
   on the deepest band (5.21:1). Steel is a light-ground token only. */
.band-navy .data, .footer .data, .hero--dark .data { color: var(--fox-amber); }
.band-abyss .data { color: var(--fox); }

.band-navy .entry, .band-abyss .entry, .hero--dark .entry, .footer .entry { border-bottom-color: var(--line-invert); }

.band-navy .link, .band-abyss .link, .hero--dark .link, .footer .link { color: var(--paper); }
.band-navy .link::after, .hero--dark .link::after { background-color: var(--fox-amber); }
.band-abyss .link::after { background-color: var(--fox); }

.band-navy .figure, .band-abyss .figure, .hero--dark .figure { color: var(--paper); }
.band-navy .figure .unit, .hero--dark .figure .unit { color: var(--fox-amber); }
.band-abyss .figure .unit { color: var(--fox); }

.band-navy .btn--ghost, .band-abyss .btn--ghost, .hero--dark .btn--ghost { color: var(--paper); border-color: var(--line-invert); }
.band-navy .exp, .band-abyss .exp, .hero--dark .exp { border-top-color: var(--line-invert); }
.band-navy .exp__sign, .band-abyss .exp__sign, .hero--dark .exp__sign { border-color: var(--line-invert); }
.band-navy .tags li, .band-abyss .tags li, .hero--dark .tags li { color: var(--mist); border-color: var(--line-invert); }

.band-navy .rule, .hero--dark .rule, .footer .rule {
  background-image:
    linear-gradient(to right, var(--fox-amber) 0 68px, transparent 68px),
    linear-gradient(to right, var(--line-invert) 0 1px, transparent 1px);
}
.band-abyss .rule {
  background-image:
    linear-gradient(to right, var(--fox) 0 68px, transparent 68px),
    linear-gradient(to right, var(--abyss-line) 0 1px, transparent 1px);
}

/* On dark, the primary button inverts. A filled navy button on a navy band is
   almost invisible, so it becomes the accent fill with ink text (5.21:1). */
.band-navy .btn:not(.btn--ghost),
.band-abyss .btn:not(.btn--ghost),
.hero--dark .btn:not(.btn--ghost) {
  background-color: var(--fox);
  border-color: var(--fox);
  color: var(--navy-deep);
}
@media (hover: hover) and (pointer: fine) {
  .band-navy .btn:not(.btn--ghost):hover,
  .band-abyss .btn:not(.btn--ghost):hover,
  .hero--dark .btn:not(.btn--ghost):hover {
    background-color: var(--paper);
    border-color: var(--paper);
    color: var(--navy-deep);
  }
}

/* ══ Texture: the fox field ════════════════════════════════════
   Mutation: topographic-pattern, reimagined. The wave contours are gone.
   This is a minimal fox head (two ears tapering to a chin) scattered at
   three scales and slight rotations, so it reads as an ambient fox motif
   rather than as the logo used as wallpaper. Navy on light surfaces, fox
   orange on dark ones. Opacity is kept low enough to be texture, not pattern. */
.foxfield {
  position: relative;
  isolation: isolate;
  /* The motif layer is inset negatively so pointer drift never exposes an
     edge. Without clipping here that bleed becomes real page width: at 1440px
     the -8% inset added exactly 115px of horizontal scroll. */
  overflow: hidden;
}
.foxfield::before {
  content: '';
  position: absolute;
  inset: -8%;
  z-index: -1;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='290' height='290' viewBox='0 0 290 290'%3E%3Cg fill='%231e2a5a'%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.11' transform='translate(22,20)'/%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.08' transform='translate(196,146) rotate(-9) scale(0.62)'/%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.095' transform='translate(96,176) rotate(11) scale(0.42)'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 290px 290px;
  will-change: transform;
}
.band-navy.foxfield::before,
.band-abyss.foxfield::before,
.hero--dark.foxfield::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='290' height='290' viewBox='0 0 290 290'%3E%3Cg fill='%23f0901a'%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.075' transform='translate(22,20)'/%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.05' transform='translate(196,146) rotate(-9) scale(0.62)'/%3E%3Cpath d='M 7 4 L 20 24 L 44 24 L 57 4 L 56 31 L 32 54 L 8 31 Z' opacity='.065' transform='translate(96,176) rotate(11) scale(0.42)'/%3E%3C/g%3E%3C/svg%3E");
}

/* ══ Typography roles ══════════════════════════════════════════ */
.display {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--step-hero);
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
}
/* Display at heading scale. Same reason as .figure--word: a Tailwind
   font-size utility cannot beat .display on an equal-specificity tie. */
.display--md { font-size: var(--step-1); }

/* The heading classes carry family, weight, tracking and leading, not just
   size, so they behave identically on a <span> as on an <h2>. Without this
   a .h2 on a span silently inherits body weight from its parent. */
.h1, .h2, .h3 {
  font-family: var(--font-display);
  font-weight: var(--heading-weight);
  letter-spacing: var(--heading-tracking);
  line-height: var(--heading-leading);
}
.h1 { font-size: var(--step-1); }
.h2 { font-size: var(--step-2); }
.h3 { font-size: var(--step-3); }
.h4 { font-size: var(--step-4); font-weight: 600; font-family: var(--font-body); letter-spacing: 0; }

.lede {
  font-size: clamp(1.125rem, 1rem + 0.55vw, 1.375rem);
  line-height: 1.55;
  color: var(--slate);
  max-width: var(--measure);
}
.band-navy .lede { color: var(--mist); }

.muted { color: var(--slate); }
.band-navy .muted { color: var(--mist); }

/* The mono data layer. Mutation: accent-font.
   Reserved for measured facts: figures, clearances, locations, counts.
   Never used for decoration and never dropped inside a heading. */
.data {
  font-family: var(--font-data);
  font-size: 0.8125rem;      /* 13px, above the 12px floor */
  letter-spacing: var(--data-tracking);
  text-transform: uppercase;
  color: var(--steel);
  font-weight: 700;          /* carries the label role now that family cannot */
}
.band-navy .data { color: var(--fox-amber); }

.figure {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.5rem, 1.5rem + 3.6vw, 4.25rem);
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.band-navy .figure { color: var(--paper); }
.figure .unit { color: var(--fox); font-size: 0.5em; }
/* Fox orange measures 3.06:1 on paper, which clears AA-large, but only 2.83:1
   on the second surface, which does not. The unit carries meaning (9 versus
   9%), so on the darker light surface it steps down to rust at 4.76:1. Same
   trap as the dark-surface contract: a token that passes on one surface can
   fail on another. */
.section--surface .figure .unit { color: var(--rust); }
.band-navy .figure .unit { color: var(--fox-amber); }

/* A figure whose value is a word rather than a number. Declared here, not as
   a Tailwind arbitrary value: the CDN injects its stylesheet ABOVE this file,
   so on an equal-specificity tie these component rules always win and a
   utility override silently does nothing. */
.figure--word {
  font-size: clamp(1.6rem, 1.1rem + 1.5vw, 2.25rem);
  letter-spacing: -0.02em;
  padding-top: 12px;
}

/* ══ The measured rule ═════════════════════════════════════════
   A chart scale bar. This is the site's sectioning device, used in
   place of the uppercase tracked eyebrow above every section, which
   is the 2023-era AI tell the anti-pattern set calls out. */
.rule {
  display: block;
  width: 100%;
  height: 9px;
  border: 0;
  margin: 0 0 var(--s-4);
  background-image:
    linear-gradient(to right, var(--fox) 0 68px, transparent 68px),
    linear-gradient(to right, var(--line-strong) 0 1px, transparent 1px);
  background-size: 100% 2px, 17px 9px;
  background-position: left top, left top;
  background-repeat: no-repeat, repeat-x;
}
.band-navy .rule {
  background-image:
    linear-gradient(to right, var(--fox-amber) 0 68px, transparent 68px),
    linear-gradient(to right, var(--line-invert) 0 1px, transparent 1px);
}

/* ══ Components: editorial-understated ═════════════════════════
   Zero radius. No shadows. Bottom rule only. Hover is a weight shift,
   which is layout-stable because the weight change is compensated. */
.entry {
  border: 0;
  border-bottom: 1px solid var(--line);
  padding-block: var(--s-4);
  background: transparent;
}
.band-navy .entry { border-bottom-color: var(--line-invert); }

/* ══ Service block ════════════════════════════════════════════
   The repeatable unit behind the extensible services architecture.
   A new service line is one more <article class="svc">. Nothing here
   or in the markup around it has to change to accept it. */
.svc {
  padding-block: var(--s-6);
  border-top: 1px solid var(--line);
}
.svc:first-child { border-top: 0; padding-top: 0; }
.svc--lead { border-top: 2px solid var(--fox); padding-top: var(--s-5); }

/* Capability keywords. Outlined and square, per editorial-understated.
   Deliberately not pills: pill-playful conflicts with this component set. */
.tags {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.tags li {
  font-family: var(--font-data);
  font-size: 12.5px;
  letter-spacing: 0.03em;
  color: var(--slate);
  border: 1px solid var(--line-strong);
  border-radius: 0;
  padding: 7px 11px;
  white-space: nowrap;
}
.band-navy .tags li { color: var(--mist); border-color: var(--line-invert); }

/* ══ Buttons: three tiers, each internally consistent ══════════ */
/* Tier 1, primary. The single documented deviation from the
   underline-reveal archetype: a recruiting-first site whose only
   conversion action is a ghost link would fail the brief. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 15px 28px;
  border-radius: 0;
  border: 1px solid var(--navy);
  background-color: var(--navy);
  color: var(--paper);            /* 12.77:1 */
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--control-duration) var(--ease-out),
              border-color var(--control-duration) var(--ease-out),
              color var(--control-duration) var(--ease-out),
              transform var(--control-duration) var(--ease-out);
}
.btn .arrow { transition: transform var(--control-duration) var(--ease-out); }
.btn:active { transform: scale(0.97); }

@media (hover: hover) and (pointer: fine) {
  .btn:hover {
    background-color: var(--fox);
    border-color: var(--fox);
    color: var(--navy-deep);       /* 5.21:1. Ink on orange, never white. */
  }
  .btn:hover .arrow { transform: translateX(5px); }
}

/* Tier 2, secondary. Outlined, same geometry as tier 1. */
.btn--ghost {
  background-color: transparent;
  color: var(--navy);
  border-color: var(--line-strong);
}
.band-navy .btn--ghost { color: var(--paper); border-color: var(--line-invert); }
@media (hover: hover) and (pointer: fine) {
  .btn--ghost:hover {
    background-color: transparent;
    border-color: var(--fox);
    color: var(--rust);
  }
  .band-navy .btn--ghost:hover { color: var(--fox-amber); }
}

/* Tier 3, tertiary. The underline-reveal archetype proper. */
.link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  position: relative;
  font-weight: 500;
  font-size: 16px;
  letter-spacing: 0.01em;
  padding: 8px 0;
  text-decoration: none;
  color: var(--ink);
}
.link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 2px;
  height: 2px;
  width: 100%;
  background-color: var(--fox);
  /* The underline-reveal archetype describes this as width 0 to 100%, but
     animating width forces layout on every frame. scaleX from a left origin
     is visually identical and stays on the compositor. */
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 300ms var(--ease-out);
}
.link .arrow { transition: transform var(--control-duration) var(--ease-out); }
.link:active { transform: scale(0.97); }
.band-navy .link { color: var(--paper); }
.band-navy .link::after { background-color: var(--fox-amber); }
@media (hover: hover) and (pointer: fine) {
  .link:hover::after { transform: scaleX(1); }
  .link:hover .arrow { transform: translateX(5px); }
}
.link:focus-visible::after { transform: scaleX(1); }

/* ══ Navigation: solid-minimal ═════════════════════════════════
   Rebuilt as a masthead. The logo is a light-on-dark lockup whose wordmark is
   #CCCCCC, invisible on the paper ground, and the brand lock forbids altering
   it. Rather than apologise for that with a small badge, the dark ground it
   needs becomes an architectural block: full header height, running to the
   viewport edge, carrying the mark at proper size. The constraint turned into
   the strongest element in the header. */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background-color: var(--paper);
  /* Clips the masthead's leftward bleed. `clip` rather than `hidden` so no
     scroll container is created under a position:sticky element. */
  overflow-x: clip;
}
/* Hairline under the header; the fox-orange segment lives on the masthead
   itself so it is exactly the block's width rather than a guessed value. */
.nav::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background-color: var(--line);
}

.nav__inner {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 104px;
  /* Only the left is cancelled, so the masthead can run to the viewport edge.
     The right keeps the shell padding or the CTA clips off-screen. */
  padding-left: 0;
}
/* .shell supplies the page padding; the masthead cancels it on the left so the
   navy runs to the edge, then pays it back as its own padding so the mark
   still aligns to the content grid. */

.nav__logo {
  display: inline-flex;
  align-items: center;
  flex: none;
  background-color: var(--navy);
  padding-inline: var(--pad-x) clamp(22px, 3vw, 40px);
  position: relative;
  border-bottom: 3px solid var(--fox);
}
/* Above 1440 the shell centres, so padding-left:0 alone left the masthead
   starting at the content edge instead of the viewport edge. This paints the
   block leftward to the edge at every width. The last 3px carries the same
   fox border so the two align exactly. */
.nav__logo::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: -3px;
  right: 100%;
  width: 100vw;
  background: linear-gradient(to bottom,
    var(--navy) calc(100% - 3px), var(--fox) calc(100% - 3px));
}
.nav__logo img {
  height: clamp(42px, 5.6vw, 62px);
  width: auto;
  display: block;
}
/* The tail motif hangs off the masthead's inner edge, so the block reads as
   part of the identity rather than as a container. */
.nav__logo::after {
  content: '';
  position: absolute;
  right: 0; top: 0; bottom: 0;
  width: 3px;
  background: linear-gradient(to bottom, var(--fox-amber), var(--fox) 55%, transparent);
}

/* The links are an index strip, not a row of text. Each is a full-height cell
   divided by hairlines, with a tick at the head of the cell. It rhymes with the
   masthead block beside it and with the measured rule used throughout, so the
   header reads as one instrument rather than a logo plus some links. */
.nav__links {
  display: none;
  align-items: stretch;
  gap: 0;
}
@media (min-width: 1024px) { .nav__links { display: flex; } }

.nav__link {
  display: flex;
  align-items: center;
  padding-inline: clamp(15px, 1.7vw, 28px);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--slate);
  border-left: 1px solid var(--line);
  position: relative;
  transition: color var(--control-duration) var(--ease-out),
              background-color var(--control-duration) var(--ease-out);
}
.nav__link:last-of-type { border-right: 1px solid var(--line); }

/* Tick at the head of each cell, the same device as the chart rule. */
.nav__link::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 1px;
  /* Drawn at full height and scaled down, rather than transitioning height.
     Animating height forces layout every frame; scaleY stays on the
     compositor and looks identical. */
  height: 14px;
  background-color: var(--line-strong);
  transform: translateX(-50%) scaleY(0.57);
  transform-origin: 50% 0;
  transition: transform var(--control-duration) var(--ease-out),
              background-color var(--control-duration) var(--ease-out);
}
/* Bottom bar, scaled from the left so it wipes in. */
.nav__link::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background-color: var(--fox);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 260ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .nav__link:hover { color: var(--ink); background-color: rgba(21, 27, 46, 0.035); }
  .nav__link:hover::before { transform: translateX(-50%) scaleY(1); background-color: var(--fox); }
  .nav__link:hover::after { transform: scaleX(1); }
}

.nav__link[aria-current] { color: var(--ink); }
.nav__link[aria-current]::before { transform: translateX(-50%) scaleY(1); background-color: var(--fox); }
.nav__link[aria-current]::after { transform: scaleX(1); }

/* The CTA is not a cell. It stands apart from the strip. */
.nav__links .btn {
  align-self: center;
  margin-left: clamp(16px, 2vw, 30px);
}

.nav__toggle {
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--control-duration) var(--ease-out),
              transform var(--control-duration) var(--ease-out);
}
.nav__toggle:active { transform: scale(0.97); }
@media (min-width: 1024px) { .nav__toggle { display: none; } }

/* Mobile menu: full-viewport overlay, explicit close button. */
/* ── Mobile menu ──────────────────────────────────────────────
   Light, not navy. The site's whole position is that it refuses to go dark
   in a corridor where everyone else does, and a navy overlay dropping out of
   a light header contradicted that. It is now paper, carrying the fox motif,
   with the links arriving on a stagger. Being a LIGHT surface, it is
   deliberately NOT in the dark-surface contract above. */
.mobile-menu {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  z-index: 100;
  display: none;
  flex-direction: column;
  overflow-y: auto;
  background-color: var(--paper);
  color: var(--ink);
  padding: 24px;
  padding-top: max(24px, env(safe-area-inset-top));
  padding-bottom: max(24px, env(safe-area-inset-bottom));
  isolation: isolate;
}
.mobile-menu.is-open { display: flex; }

/* The fox field, larger and a touch stronger than on a page section, so the
   overlay reads as its own place rather than as a blank sheet. */
.mobile-menu::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: var(--foxfield-light);
  background-size: 300px 300px;
  /* Lighter than on a page section. With no competing content the motif reads
     far stronger here, and it should stay texture rather than become pattern. */
  opacity: 0.55;
}

/* A fox-orange rule across the top edge, so the panel is anchored rather
   than floating. */
.mobile-menu::after {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  background: linear-gradient(to right, var(--fox) 0 38%, var(--fox-amber) 38% 62%, transparent 62%);
}

.mobile-menu__close {
  position: absolute;
  top: 16px; right: 16px;
  width: 46px; height: 46px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--control-duration) var(--ease-out),
              color var(--control-duration) var(--ease-out),
              transform var(--control-duration) var(--ease-out);
}
.mobile-menu__close:active { transform: scale(0.94) rotate(90deg); }
@media (hover: hover) and (pointer: fine) {
  .mobile-menu__close:hover { border-color: var(--fox); color: var(--rust); }
}

.mobile-menu__list {
  list-style: none;
  margin: 88px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.mobile-menu__link {
  display: flex;
  align-items: baseline;
  gap: 14px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.1rem, 1.3rem + 4.4vw, 3.1rem);
  letter-spacing: var(--display-tracking);
  line-height: 1.05;
  text-decoration: none;
  color: var(--ink);                      /* 15.95:1 on paper */
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
  position: relative;
  transition: color var(--control-duration) var(--ease-out),
              transform var(--control-duration) var(--ease-out);
}
.mobile-menu__link:active { transform: translateX(4px); }
@media (hover: hover) and (pointer: fine) {
  .mobile-menu__link:hover { color: var(--rust); }
}

/* The current item: ink text with a fox-orange bar, rather than orange text.
   Orange as display type on paper is only 3.06:1, a two percent margin over
   AA-large, which is not a margin worth relying on. */
.mobile-menu__link[aria-current] { color: var(--ink); }
.mobile-menu__link[aria-current]::before {
  content: '';
  position: absolute;
  left: -24px;
  top: 50%;
  width: 16px;
  height: 4px;
  background-color: var(--fox);
  transform: translateY(-50%);
}
.mobile-menu__link[aria-current]::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 3px;
  background-color: var(--fox);
}

/* Links arrive on a stagger when the panel opens. This is the pop. */
.mobile-menu.is-open .mobile-menu__list > li,
.mobile-menu.is-open .mobile-menu__foot > * {
  animation: menu-in 420ms var(--ease-out) both;
}
.mobile-menu.is-open .mobile-menu__list > li:nth-child(1) { animation-delay: 40ms; }
.mobile-menu.is-open .mobile-menu__list > li:nth-child(2) { animation-delay: 90ms; }
.mobile-menu.is-open .mobile-menu__list > li:nth-child(3) { animation-delay: 140ms; }
.mobile-menu.is-open .mobile-menu__list > li:nth-child(4) { animation-delay: 190ms; }
.mobile-menu.is-open .mobile-menu__foot > *:nth-child(1) { animation-delay: 250ms; }
.mobile-menu.is-open .mobile-menu__foot > *:nth-child(2) { animation-delay: 300ms; }
.mobile-menu.is-open .mobile-menu__foot > *:nth-child(3) { animation-delay: 350ms; }
@keyframes menu-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.mobile-menu__foot { margin-top: auto; padding-top: 40px; }

/* ══ Hero: offset-editorial + grid-break ═══════════════════════ */
.hero { position: relative; padding-top: var(--s-7); padding-bottom: var(--s-8); }
@media (min-width: 1024px) { .hero { padding-top: var(--s-8); } }  /* stays under the pt-24 cap */

.hero__media {
  position: relative;
  background-color: var(--surface);
  border: 1px solid var(--line);
  aspect-ratio: 4 / 5;
  max-height: 520px;
  overflow: hidden;
}
.hero__media img { width: 100%; height: 100%; object-fit: cover; }

/* grid-break: on desktop the portrait escapes the container to the
   viewport edge while the headline column stays held. Height is capped
   rather than left to the aspect ratio, which at a 7-column span would
   drive the hero past 1200px and push everything below the fold. */
@media (min-width: 1024px) {
  .hero__media {
    margin-right: calc(-1 * var(--pad-x));
    aspect-ratio: auto;
    height: min(74vh, 640px);
    max-height: none;
  }
}
@media (min-width: 1440px) {
  .hero__media { margin-right: calc((100vw - var(--maxw)) / -2 - var(--pad-x)); }
}

/* ══ Photography placeholders ══════════════════════════════════
   Marked, not fabricated. Each carries a shot brief. */
.shot {
  position: relative;
  background-color: var(--surface);
  border: 1px solid var(--line);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}
.shot::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(135deg, transparent 0 49.6%, var(--line) 49.6% 50.4%, transparent 50.4%),
    linear-gradient(45deg, transparent 0 49.6%, var(--line) 49.6% 50.4%, transparent 50.4%);
  opacity: 0.5;
}
.shot__brief {
  position: relative;
  padding: var(--s-3);
  font-family: var(--font-data);
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--slate);
  background-color: color-mix(in srgb, var(--paper) 92%, transparent);
  border-top: 1px solid var(--line);
  width: 100%;
}
.shot__brief b { color: var(--rust); font-weight: 500; text-transform: uppercase; }

/* ══ Entrances: fade-only, per subtle-elegant ══════════════════
   The signature moment. Section text cross-fades in gently enough
   that the reader notices the content, never the animation. */
[data-animate] {
  opacity: 0;
  transition: opacity var(--enter-duration) var(--ease-in-out);
}
[data-animate].is-in { opacity: 1; }
[data-animate-group] > * { transition-delay: calc(var(--i, 0) * var(--stagger)); }

/* The set-piece: the fox tail curve draws itself once, on load.
   Derived from the existing mark's tail. The logo itself is untouched. */
.tail-draw path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  pathLength: 1;
  animation: tail-draw 1600ms var(--ease-out) 260ms forwards;
}
@keyframes tail-draw { to { stroke-dashoffset: 0; } }


/* ══ Scroll progress ══════════════════════════════════════════
   A chart scale that fills as you read. scaleX only, so it never
   touches layout. */
.progress {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  height: 3px;
  z-index: 60;
  background: transparent;
  pointer-events: none;
}
.progress__bar {
  height: 100%;
  background: var(--fox);
  transform: scaleX(0);
  transform-origin: left center;
  will-change: transform;
}

/* ══ Pointer-reactive fox field ═══════════════════════════════
   The motif layer drifts a few pixels against the pointer. Transform
   only, gated to fine pointers, and disabled under reduced motion. */
@media (hover: hover) and (pointer: fine) {
  .foxfield--live::before {
    transition: transform 600ms var(--ease-out);
    transform: translate3d(var(--px, 0px), var(--py, 0px), 0);
  }
}

/* ══ Hero, dark ═══════════════════════════════════════════════ */
.hero--dark {
  background-color: var(--abyss);
  color: var(--paper);
}
.hero--dark .display,
.hero--dark h1 { color: var(--paper); }

/* ══ Hero background video ════════════════════════════════════
   The scrim below is not a taste decision. Sampling all 25 seconds of the
   source found pure-white pixels (relative luminance 1.0) in the node
   highlights, so unscrimmed hero text would sit at roughly 1:1 in places.
   Minimum alpha of #151b2e over a pure-white pixel to reach 4.5:1:
     paper #f6f7f9 -> 0.620    mist #b9c4da -> 0.765
     amber #f0901a -> 0.855    fox  #f0601e -> 0.950
   The scrim is a flat 0.66 at every breakpoint so the video reads as one
   background across the whole hero. Every string over it is paper (floor
   0.620) or sits on the archival plate, which composites its own backing. Fox
   orange is never used as text over video because its floor is 0.950.
   Because of that 0.95 figure, fox orange is never used as text over video. */
.hero__video {
  position: absolute;
  inset: 0;
  z-index: -2;                 /* under the fox field, over the section fill */
  overflow: hidden;
  pointer-events: none;
}
.hero__video video,
.hero__video img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero__video::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Uniform, so the video reads as a background for the whole hero rather
     than an accent on one side. Flat 0.66 puts about a third of the video
     through everywhere. This is only affordable because the hero lede runs
     on paper rather than mist: paper's floor over a pure-white video pixel
     is 0.620, mist's is 0.765, and a mist lede would have forced 0.80 across
     the full width and shown barely a fifth. Same value at every breakpoint,
     which also removes the stacked-versus-two-column hazard entirely. */
  background: rgba(21, 27, 46, 0.66);
}
/* A vignette so the video never fights the nav or the section edge. */
.hero__video::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to bottom, rgba(21,27,46,0.45) 0%, rgba(21,27,46,0) 14%),
    linear-gradient(to top, rgba(21,27,46,0.70) 0%, rgba(21,27,46,0) 20%);
}

/* Captions sitting over the video use paper, not the amber data colour.
   Amber needs a 0.855 scrim to clear AA over a white pixel and the right
   edge of the gradient is 0.74; paper only needs 0.62. */
/* The hero lede runs on paper, not the mist used on other dark bands. Over
   video, mist needs an 0.765 scrim and paper needs 0.620, and that difference
   is the entire budget for a full-bleed uniform background. Declared after the
   dark-surface contract so it wins on source order at equal specificity. */
.hero--dark .lede { color: var(--paper); }

/* ── Circular photo frame ─────────────────────────────────────
   Used where a photograph needs the porthole language without the full
   archival plate. Capped via --porthole-max so it is never displayed larger
   than its source can support at 2x. Set that per instance. */
.porthole {
  position: relative;
  width: min(100%, var(--porthole-max, 250px));
  margin-inline: auto;
  aspect-ratio: 1;
  isolation: isolate;
}
.porthole img {
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: 50%;
  transition: transform 700ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .porthole:hover img { transform: scale(1.04); }
}
.porthole__ring,
.porthole__ring::before {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}
.porthole__ring {
  inset: -1px;
  border: 1px solid var(--line-strong);
}
.porthole__ring::before {
  content: '';
  inset: -14px;
  border: 1px dashed rgba(240, 96, 30, 0.5);
}
.band-navy .porthole__ring,
.band-abyss .porthole__ring,
.hero--dark .porthole__ring { border-color: rgba(246, 247, 249, 0.28); }

/* ── Archival plate ───────────────────────────────────────────
   The client's original is 500x501, confirmed against the WordPress media API.
   The shipped asset is a 1000px iterative upscale: four 1.18x LANCZOS passes
   with a light sharpen between each, which beat a single 2x jump and a naive
   browser upscale on the hardest detail in the frame (the SLY FOX lettering on
   the transom). At a 360px display that is 1000 source px against 720 device
   px on a 2x screen, so the browser is downsampling rather than stretching.

   Presence comes from the plate around it rather than from stretching the
   photograph past its resolution: an instrument bezel, a measured rule, and
   the place and coordinates set as data. */
.plate {
  margin: 0;
  width: min(100%, 470px);
  margin-inline: auto;
  padding: 34px 30px 30px;
  background-color: rgba(21, 27, 46, 0.62);
  border: 1px solid var(--line-invert);
  backdrop-filter: blur(3px);
  text-align: center;
}
/* Composited over the hero's 0.66 scrim this backing reaches 0.87, which
   clears amber's 0.855 floor over a white video pixel, so the data layer can
   keep its colour here. */

.plate__frame {
  position: relative;
  width: min(100%, clamp(240px, 26vw, 360px));
  margin-inline: auto;
  aspect-ratio: 1;
  isolation: isolate;
}
.plate__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  transition: transform 700ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .plate:hover .plate__frame img { transform: scale(1.04); }
}

/* Instrument bezel: a fine inner keyline, a dashed outer ring, and four tick
   marks at the quarters. */
.plate__ring,
.plate__ring::before,
.plate__ring::after {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}
.plate__ring {
  inset: -1px;
  border: 1px solid rgba(246, 247, 249, 0.28);
}
.plate__ring::before {
  content: '';
  inset: -13px;
  border: 1px dashed rgba(240, 144, 26, 0.55);
}
.plate__ring::after {
  content: '';
  inset: -22px;
  border: 1px solid rgba(246, 247, 249, 0.10);
}
.plate__ticks {
  position: absolute;
  inset: -22px;
  pointer-events: none;
}
.plate__ticks span {
  position: absolute;
  inset: 0;
  display: block;
}
/* Each span fills the ring box and carries its mark on the ::before, so
   rotating the span about its OWN centre swings the mark around the circle.
   Rotating the mark directly needs a transform-origin expressed in px, which
   breaks the moment the frame size changes. */
.plate__ticks span::before {
  content: '';
  position: absolute;
  top: -5px;
  left: 50%;
  width: 1px;
  height: 10px;
  background: var(--fox);
  transform: translateX(-50%);
}
.plate__ticks span:nth-child(2) { transform: rotate(90deg); }
.plate__ticks span:nth-child(3) { transform: rotate(180deg); }
.plate__ticks span:nth-child(4) { transform: rotate(270deg); }

.plate__cap { margin-top: 34px; }
.plate__cap .rule { margin-bottom: 16px; }
.plate__name {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.0625rem;
  letter-spacing: 0.02em;
  color: var(--paper);
}
.plate__place {
  display: block;
  margin-top: 5px;
  font-size: 0.9375rem;
  color: var(--mist);
}
.plate__coord {
  display: block;
  margin-top: 12px;
  font-size: 0.75rem;
  letter-spacing: 0.16em;
}

/* The same plate on a light section. Every colour flips; leaving it out would
   have shipped paper text on paper. */
.plate--light {
  background-color: rgba(255, 255, 255, 0.5);
  border-color: var(--line);
}
.plate--light .plate__ring { border-color: var(--line-strong); }
.plate--light .plate__ring::before { border-color: rgba(240, 96, 30, 0.5); }
.plate--light .plate__ring::after { border-color: rgba(21, 27, 46, 0.10); }
.plate--light .plate__name { color: var(--ink); }
.plate--light .plate__place { color: var(--slate); }
.plate--light .plate__coord { color: var(--steel); }

/* ══ Expandable competency ════════════════════════════════════
   The panel is toggled, not animated open: only opacity and a small
   translate are transitioned, so no layout property is ever tweened. */
.exp { border-top: 1px solid var(--line); }
.band-abyss .exp, .band-navy .exp { border-top-color: var(--abyss-line); }
.exp:first-of-type { border-top: 2px solid var(--fox); }

.exp__head {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--s-3);
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: var(--s-4) 0;
  cursor: pointer;
  color: inherit;
  font-family: inherit;
  transition: opacity var(--hover-duration) var(--ease-out);
}
.exp__head .h2 { font-weight: 600; }
.exp__head:active { transform: scale(0.995); }
@media (hover: hover) and (pointer: fine) {
  .exp__head:hover { opacity: 0.72; }
}

.exp__sign {
  position: relative;
  width: 34px; height: 34px;
  flex: none;
  border: 1px solid var(--line-strong);
}
.band-abyss .exp__sign, .band-navy .exp__sign { border-color: var(--line-invert); }
.exp__sign::before, .exp__sign::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 13px; height: 1.5px;
  background: var(--fox);
  transform: translate(-50%, -50%);
  transition: transform 260ms var(--ease-out), opacity 260ms var(--ease-out);
}
.exp__sign::after { transform: translate(-50%, -50%) rotate(90deg); }
.exp[open] .exp__sign::after,
.exp__head[aria-expanded="true"] .exp__sign::after { transform: translate(-50%, -50%) rotate(0deg); opacity: 0; }

.exp__panel { display: none; padding-bottom: var(--s-5); }
.exp__panel.is-open { display: block; }
.exp__panel.is-open > * {
  animation: exp-in 380ms var(--ease-out) both;
}
@keyframes exp-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ══ Testimonials ═════════════════════════════════════════════ */
.quote { border-top: 1px solid var(--abyss-line); padding-top: var(--s-4); }
.quote__mark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 3rem;
  line-height: 0.7;
  color: var(--fox);
  display: block;
  margin-bottom: 10px;
}
.quote__text {
  font-size: 1.0625rem;
  line-height: 1.62;
  font-style: italic;
  color: var(--mist);
  max-width: 46ch;
}
.quote__who {
  display: flex;
  align-items: center;
  gap: 14px;
  font-weight: 700;
  font-size: 0.875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fox);
  margin-top: var(--s-3);
}


/* ══ Quote avatar ═════════════════════════════════════════════
   SlyFox has no photographs of the people who gave these testimonials. The
   five testimonial assets on the live site are quote text rendered as images,
   not portraits. Rather than fake a face or leave the attribution bare, each
   quote gets a real portrait slot carrying a monogram, framed with the same
   ring language as the boat porthole.

   TO DROP IN A REAL PHOTO: replace the monogram text node with
   <img src="assets/img/slyfoxian-adam.webp" alt="Adam, Solutions Architect">
   and nothing else has to change. The img rule below is already here. */
.avatar {
  position: relative;
  flex: none;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 19px;
  letter-spacing: 0;
  color: var(--paper);                       /* 15.95:1 on the abyss band */
  background-color: rgba(246, 247, 249, 0.09);
  border: 1px solid rgba(246, 247, 249, 0.20);
  overflow: hidden;
}
.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}
/* The tail arc, echoing the porthole ring. Drawn outside the circle so it
   survives a photo being dropped in. */
.avatar::after {
  content: '';
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 1px solid transparent;
  border-top-color: var(--fox);
  border-right-color: var(--fox-amber);
  transform: rotate(-32deg);
}

/* ══ Real photography ═════════════════════════════════════════ */
.portrait {
  position: relative;
  overflow: hidden;
  background-color: var(--surface);
  border: 1px solid var(--line);
  /* All five real portraits are 538x675. Using their true 4:5 ratio keeps
     heads framed instead of cropping them to a landscape box. */
  aspect-ratio: 4 / 5;
}
.portrait img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 600ms var(--ease-out), filter 400ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .portrait:hover img { transform: scale(1.045); }
}

/* Charity logos. Real marks, normalised to one optical weight. */
.charities {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4) var(--s-5);
}
.charities img {
  height: 44px;
  width: auto;
  opacity: 0.9;
  transition: opacity var(--hover-duration) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .charities a:hover img, .charities img:hover { opacity: 1; }
}

/* ══ Forms ════════════════════════════════════════════════════ */
.field { display: block; margin-bottom: var(--s-4); }
.field__label {
  display: block;
  font-family: var(--font-data);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--steel);
  margin-bottom: 8px;
}
.field__input,
.field__select,
.field__textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink);
  background-color: transparent;
  border: 0;
  border-bottom: 1px solid var(--line-strong);
  border-radius: 0;
  padding: 12px 0;
  transition: border-color var(--control-duration) var(--ease-out);
}
.field__textarea { resize: vertical; min-height: 132px; }
.field__input:focus,
.field__select:focus,
.field__textarea:focus {
  outline: none;
  border-bottom: 2px solid var(--fox);
  padding-bottom: 11px;
}

/* ══ Footer ═══════════════════════════════════════════════════ */
.footer { background-color: var(--navy); color: var(--paper); }
.footer a { color: var(--mist); text-decoration: none; }

/* Footer nav links were rendering as bare 18px-tall text, under the 24px
   WCAG 2.2 target-size minimum and well under a comfortable thumb. They get
   real vertical padding, generous below the desktop breakpoint where taps
   actually happen, and the list gap is dropped there since the padding now
   provides the separation. */
.footer nav a { display: inline-block; padding-block: 3px; }
@media (max-width: 1023px) {
  .footer nav a { padding-block: 13px; }
  .footer nav ul { gap: 0; }
}
@media (hover: hover) and (pointer: fine) {
  .footer a:hover { color: var(--fox-amber); }
}

/* ══ Reduced motion ═══════════════════════════════════════════
   Gentler, not zero: opacity and colour survive, movement does not.
   Scroll-driven rules are nulled explicitly, since they are not
   duration-based and a duration reset alone will not stop them. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  [data-parallax], [class*="parallax"], .zoom-element, .scroll-rotate {
    animation: none !important;
    animation-timeline: none !important;
    transform: none !important;
  }
  [data-animate] { opacity: 1 !important; }
  .tail-draw path { animation: none !important; stroke-dashoffset: 0 !important; }
  .btn:hover .arrow, .link:hover .arrow { transform: none !important; }
  .foxfield--live::before { transform: none !important; transition: none !important; }
  .hero__video video { display: none; }
  .porthole img, .portrait img, .plate__frame img { transform: none !important; }
  .exp__panel.is-open > * { animation: none !important; opacity: 1 !important; transform: none !important; }
  .mobile-menu.is-open .mobile-menu__list > li,
  .mobile-menu.is-open .mobile-menu__foot > * { animation: none !important; opacity: 1 !important; transform: none !important; }
  .mobile-menu__close:active { transform: none !important; }
  .mobile-menu__link:active { transform: none !important; }
}
