/* ==========================================================================
   Oakes Consulting — site stylesheet

   Extracted from index.njk on 2026-09-02. It had grown to 326 lines inlined
   in the home page, which was fine for one page and would have meant five
   drifting copies once the interior pages existed.

   The .dirB- prefix is a fossil of the exploration this design won as
   "direction B". Left alone on purpose: renaming every selector across every
   page is a large diff with no user-visible benefit, and the risk of a missed
   selector is real. It is a name, and the name is documented here.

   Color was measured, never estimated:
     paper #F4F5F6 on black #0B0D0F = 17.83
     muted #9AA1A8 on black         =  7.45   AA / AAA
     gold  #C9A227 on black         =  8.05   AA / AAA
     steel #4A88B5 on black         =  5.08   AA
   On the light section ground #F4F5F6:
     ink   #16181A = 16.31   muted #565D63 = 6.12   gold #8A6B21 = 4.57
   Gold FAILS AA for body text on any light ground. On light it is rules and
   display type only; on the dark ground the restriction lifts.
   ========================================================================== */

/* ==========================================================================
   Direction B — dark and serious
   ========================================================================== */

/* The browser's default 8px body margin was never reset. It inset every
   content column by 8px while the fixed header, which resolves against the
   viewport rather than the body box, ignored it. That is where the 8px
   mismatch between the logo and the body content came from below 1400px.
   Resetting it moves all content 8px left and puts both on the same origin. */
body { margin: 0; }

.dirB {
  --black:    #0B0D0F;
  --black-2:  #14171A;   /* raised card on the dark ground */
  --paper:    #F4F5F6;
  --ink:      #16181A;

  --muted:      #9AA1A8;  /* on dark  */
  --muted-lite: #565D63;  /* on light */

  /* Gold clears AAA on the dark ground at 8.05, so on dark it may be text.
     In the light section it drops to 4.57 at the deepened value and fails
     entirely at the bright one, so light-section gold is the deep cut and is
     still kept off small print. */
  --gold:      #C9A227;
  --gold-deep: #8A6B21;
  --steel:     #4A88B5;

  --hair:      rgba(244, 245, 246, 0.14);
  /* Interactive borders, which are held to a different standard than the
     decorative hairlines above. WCAG 1.4.11 wants 3.0 for the boundary of a UI
     component. At the --hair value a ghost button's edge measured 1.42 on the
     page ground and 1.53 over the hero tree, failing by more than half. At .45
     it measures 4.24 and 3.94. Deliberately short of the label's own color:
     a full-white edge measures 13.41 over the tree and makes the secondary
     button read as loud as the solid one beside it. */
  --control-edge: rgba(244, 245, 246, 0.45);
  --hair-lite: rgba(22, 24, 26, 0.14);

  --font-display: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --pad: clamp(1.25rem, 5vw, 4.5rem);
  --gap: clamp(4rem, 9vw, 9rem);

  background: var(--black);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 17px; line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

.dirB *, .dirB *::before, .dirB *::after { box-sizing: border-box; }
.dirB :where(p, h1, h2, h3, ul, li) { margin: 0; }
.dirB ul { list-style: none; padding: 0; }

.dirB__wrap { max-width: 1400px; margin: 0 auto; padding-inline: var(--pad); }

.dirB h1, .dirB h2, .dirB h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.02;
}

/* :where() on the scope, so this is (0,1,0) and any later single-class rule can
   recolour a specific label. As `.dirB .mono` it was (0,2,0) and outranked
   `.dirB-cap__n`, which meant the gold on the 01-04 numbers had never rendered.
   Third instance of the same trap found on 2026-09-05, all of them by reading
   computed color in a browser rather than reading this file. */
/* Inline links inside body copy. There was no rule for these at all, so they
   took the browser defaults and a visited link rendered PURPLE, which is what
   Ryan spotted on /engagement/. Steel is the site's existing link color, used
   by .dirB-more, and measures 5.08 on the dark ground and 6.63 on the light
   one, so it clears AA on both.

   :visited is set to the same value on purpose. Leaving it unset is what let
   the browser recolor it, and a link that changes color after one click is
   noise on a four-page site. */
:where(.dirB) .body a,
:where(.dirB) .dirB-note a,
:where(.dirB) .dirB-stage a,
:where(.dirB) .lede a {
  color: var(--steel);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(74, 136, 181, .45);
}
:where(.dirB) .body a:visited,
:where(.dirB) .dirB-note a:visited,
:where(.dirB) .dirB-stage a:visited,
:where(.dirB) .lede a:visited { color: var(--steel); }
.dirB-lite .body a, .dirB-lite .dirB-note a, .dirB-lite .lede a,
.dirB-lite .body a:visited, .dirB-lite .dirB-note a:visited, .dirB-lite .lede a:visited {
  color: #2B5B7E; text-decoration-color: rgba(43, 91, 126, .45);
}

:where(.dirB) .mono {
  font-family: var(--font-mono);
  font-size: 11.5px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted);
}

/* ---------- header ---------- */
/* Fixed, not absolute. An absolutely positioned header scrolls away with the
   page and has no way to come back, which is the whole reason the hide-and-
   return behavior did nothing here. Interior pages override to sticky below,
   so the bar still holds its own space in the flow there. */
.dirB-head {
  position: fixed; top: 0; left: 0; right: 0; z-index: 5;
  border-bottom: 1px solid transparent;
  transition: transform .32s var(--ease-out, cubic-bezier(0,0,.2,1)),
              background-color .32s var(--ease-out, cubic-bezier(0,0,.2,1)),
              border-color .32s var(--ease-out, cubic-bezier(0,0,.2,1));
}
/* Both applied by main.js. The controller only ever toggles a class and never
   writes an inline style, so if the script fails to load the header stays a
   perfectly usable static one rather than a hidden one. */
.dirB-head.is-hidden { transform: translateY(-100%); }
.dirB-head.is-stuck {
  background: var(--black);
  border-bottom-color: var(--hair);
}
/* max-width:none puts the logo on the left screen edge and the nav on the
   right, instead of inside the 1400px content column. The wrap's own
   padding-inline still keeps both off the glass. This is the same pattern the
   church site uses. It deliberately breaks alignment with the headline below,
   which is a decision rather than an oversight: see the note on .dirB-hero. */
.dirB-head__in {
  display: flex; align-items: center; justify-content: space-between;
  gap: 2rem; padding-block: 26px;
  max-width: none;
}
.dirB-head__mark {
  display: flex; align-items: center; gap: 11px;
  color: var(--paper); text-decoration: none;
  font-family: var(--font-display); font-weight: 600;
  font-size: 16px; letter-spacing: -0.01em; white-space: nowrap;
}
.dirB-head__mark img { width: 30px; height: auto; display: block; opacity: .95; }
.dirB-head nav { display: flex; gap: clamp(1.25rem, 2.6vw, 2.25rem); }
.dirB-head nav a {
  color: var(--muted); text-decoration: none; font-size: 14px;
  transition: color .15s;
}
.dirB-head nav a:hover { color: var(--paper); }

/* See the note in direction A: below 760 the nav overflows the viewport. */
@media (max-width: 760px) {
  .dirB-head__in { flex-direction: column; align-items: flex-start; gap: 10px; }
  .dirB-head nav { flex-wrap: wrap; gap: .75rem 1.25rem; }
}

/* ---------- hero ---------- */
/* A full viewport. Type at a scale the rejected build never went near, and the
   engraving run off the right edge rather than sat in a box. It used to be
   pure texture, cropped at the canopy; since 2026-09-05 the trunk carries on
   below the hero and stands on the rule in the section underneath, so the
   plate is now a whole tree that happens to bleed. See .dirB-hero__oak. */
.dirB-hero {
  position: relative;
  /* Clipped on the X axis only. The engraving bleeds past the right edge and
     must never give the document a horizontal scrollbar, but it now runs on
     PAST THE BOTTOM of the hero and down into the section below, so that axis
     has to stay visible. overflow-x:hidden would not do: a hidden on one axis
     forces the other to `auto`, which clips the trunk again and adds a scroll
     container. `clip` is the one value that leaves the other axis alone.

     The hidden below it is the fallback, and the order is the whole point: a
     browser without `clip` (Safari 15 and older) drops that line and keeps
     hidden, which crops the trunk at the seam exactly as this did before but
     still refuses the scrollbar. Reversing the two lines disables the effect
     everywhere. */
  overflow-x: hidden;
  overflow-x: clip; overflow-y: visible;
  min-height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
  padding-block: clamp(7rem, 14vh, 10rem) clamp(3rem, 7vh, 5rem);
}
/* The tree crosses the seam.

   It used to stop dead at the hero's bottom edge, and a bottom mask fade had
   already dissolved the trunk well above that, so the hero showed a canopy
   resting on nothing and the page read as two stacked black boxes.

   The plate carries the whole engraving: trunk, root flare, the ground strokes
   under it, and ISAIAH 61:3 cut into the base at a size nobody will read on
   purpose. So the fix is to stop hiding it. The trunk now runs past the hero
   and sets its ground strokes down just above the rule under "What we do",
   which turns that rule into the line the tree is standing on.

   Two things this depends on, both easy to undo by accident:
   - The bottom half of the old mask is GONE. It existed to fade a truncated
     trunk into the ground; the engraving's own ground strokes do that job now,
     and putting the fade back deletes the trunk again.
   - The offset is measured in PIXELS from the hero's bottom edge, not as a
     percentage. Percentages here resolve against the HERO, which is a viewport
     tall on a desktop and a different number on every phone, so a percentage
     cannot hold the trunk on a target that sits a fixed --gap below the seam.
     This is the trap that cost 152px of trunk on mobile once already. */
.dirB-hero__oak {
  position: absolute; right: -4%; width: min(1400px, 97%);
  /* Lands the plate 20px below where --gap ends. The section rule sits at
     --gap + 35px (the eyebrow's line box plus its 16px of padding), so the
     ground strokes come to rest a hair above it rather than touching. */
  bottom: calc(-1 * (var(--gap) + 20px));
  opacity: .16; pointer-events: none;
  /* Left edge only: the plate dissolves before it reaches the type column. */
  -webkit-mask-image: linear-gradient(to left, #000 55%, transparent 96%);
          mask-image: linear-gradient(to left, #000 55%, transparent 96%);
}
.dirB-hero__oak img { width: 100%; height: auto; display: block; }
/* The trunk is now inside the next section's box, and an absolutely positioned
   element paints above non-positioned in-flow text no matter how early it sits
   in the document. Making that section positioned puts its own content back on
   top, so nothing can end up reading through a branch. */
.dirB-hero + .dirB-sec { position: relative; }

/* Mobile. The vertical offset is shared with desktop now and needs no override:
   it is measured in pixels from the seam, so it lands the same distance above
   the section rule on a 390px phone as on a 1440px desktop. This is the whole
   reason it stopped being a percentage. A percentage resolved against the HERO,
   which stays ~844px tall on a phone while the plate shrinks to ~194px, so
   bottom:-18% once clipped 152px of a 194px image and left 22% of the tree.
   What still needs its own values here is the horizontal framing: the plate
   runs wider and further off the right edge so the trunk stays in frame, and
   sits a little brighter because there is less of it. */
@media (max-width: 760px) {
  .dirB-hero__oak {
    right: -20%;
    width: min(760px, 132%);
    opacity: .19;
    -webkit-mask-image: linear-gradient(to left, #000 62%, transparent 99%);
            mask-image: linear-gradient(to left, #000 62%, transparent 99%);
  }
}
/* width:100% matters. The hero is a column flex container, and .dirB__wrap
   carries `margin: 0 auto`. On a flex item those auto margins shrink the box
   to fit its contents instead of filling the 1400px column, which parked the
   whole hero 171px right of the grid line every section heading sits on.
   Stretching it puts the hero back on the same left edge as the body of the
   page. The header is deliberately not on that line any more: it was pushed
   out to the screen edges on 2026-09-03, so the logo sits left of this. */
.dirB-hero > .dirB__wrap { position: relative; z-index: 2; width: 100%; }

.dirB-hero h1 {
  font-size: clamp(2.9rem, 7.6vw, 6.6rem);
  /* Breaks the headline as "The work has outgrown / the way it is run."
     Measured in Archivo 600 at the capped size: the first line needs 18.22ch,
     and pulling "the" up onto it would need 21.03ch. Anything inside that
     window produces the break; 19.5ch sits near the middle so a fallback
     font's metrics cannot tip it either way. Both bounds scale with the font,
     so the break holds as the clamp shrinks. Measured: two lines down to a
     540px viewport, and at 530px and below the first line no longer fits the
     column and wraps further on its own, which is correct. */
  max-width: 19.5ch;
  margin-top: clamp(1.5rem, 3vh, 2.25rem);
}
.dirB-hero__lede {
  margin-top: clamp(1.5rem, 3vh, 2.25rem);
  max-width: 50ch; font-size: clamp(1.05rem, 1.4vw, 1.25rem);
  color: var(--muted); line-height: 1.55;
}

.dirB-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(2rem, 4vh, 2.75rem); }
.dirB-btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 15px 26px; border-radius: 2px;
  background: var(--paper); color: var(--black);
  font-size: 14px; font-weight: 500; text-decoration: none;
  border: 1px solid var(--paper);
  transition: background .18s, color .18s, border-color .18s;
}
/* Hover softens each button rather than inverting it.

   The old behavior swapped them: the solid one went transparent and the ghost
   went solid, so moving between the two looked like they traded identities.

   They cannot share a hover background, which was the obvious fix. A black
   label and a white label pass AA on opposite sides of the range and cross
   through the failing zone: at .45 white a black label measures 4.24 and a
   white one 4.21, and both need 4.5. There is no value that serves both.

   So each keeps its own label color and moves a short distance instead. The
   solid one dims slightly, the ghost one lifts slightly, and neither becomes
   the other. Measured: black on .88 is 13.79, white on .14 is well past 5.94. */
.dirB-btn:hover {
  background: rgba(244, 245, 246, .88);
  border-color: rgba(244, 245, 246, .88);
  color: var(--black);
}
.dirB-btn--ghost { background: transparent; color: var(--paper); border-color: var(--control-edge); }
.dirB-btn--ghost:hover {
  background: rgba(244, 245, 246, .14);
  border-color: rgba(244, 245, 246, .7);
  color: var(--paper);
}

/* ---------- section furniture ---------- */
.dirB-sec { padding-block: var(--gap); }
/* Section numbers were removed on 2026-09-03. They numbered things that were
   not a sequence, the scheme had already broken (engagement.njk opened on an
   orphaned "05"), and the mono/caps/zero-padded-number stack is the loudest
   generated-looking element on a page. The eyebrow label stays: it says what
   the section is. The .01-.04 on the service cards also stay, because the copy
   states they are listed in the order they happen, so those numbers inform. */
.dirB-secmark {
  display: flex; align-items: baseline;
  padding-bottom: 16px; margin-bottom: clamp(2.25rem, 4.5vw, 3.5rem);
  border-bottom: 1px solid var(--hair);
}
.dirB-lite .dirB-secmark { border-bottom-color: var(--hair-lite); }
.dirB-lite .dirB-secmark .mono { color: var(--muted-lite); }

.dirB h2 { font-size: clamp(1.85rem, 3.7vw, 3.1rem); max-width: 21ch; }
/* A wider measure for headings meant to land on one line. "Four things. Most
   people need one of them." measures 33.4ch, against the 21ch global cap that
   was wrapping it. Scoped as a modifier rather than raising that cap: at 35ch
   ten other headings across four pages reflow, and several of them read better
   on two lines. Still a max-width, so narrow viewports wrap normally. */
.dirB h2.dirB-h2--wide { max-width: 35ch; }
.dirB h3 { font-size: clamp(1.25rem, 1.9vw, 1.6rem); letter-spacing: -0.02em; }
.dirB .lede { font-size: clamp(1rem, 1.3vw, 1.18rem); color: var(--muted); max-width: 54ch; line-height: 1.55; }
.dirB-lite .lede { color: var(--muted-lite); }
.dirB .body { max-width: 62ch; }
.dirB .body p + p { margin-top: 1.1em; }

/* ---------- capabilities: the unequal editorial grid, on dark ---------- */
/* Ryan's call, 2026-09-02: keep direction C's unequal grid here rather than
   the Palantir-style ruled list, because the different-sized panels "add some
   really interesting context." He also liked the pop the ruled list had on
   hover, so that behavior is carried across rather than dropped.
   Four equal boxes is the single most template-looking arrangement there is;
   the 4/2 - 2/4 split is what makes it read as designed. */
.dirB-grid {
  display: grid; gap: 1px; background: var(--hair);
  grid-template-columns: 1fr;
  border-block: 1px solid var(--hair);
}
@media (min-width: 820px) {
  .dirB-grid { grid-template-columns: repeat(6, 1fr); }
  .dirB-grid > article:nth-child(1) { grid-column: span 4; }
  .dirB-grid > article:nth-child(2) { grid-column: span 2; }
  .dirB-grid > article:nth-child(3) { grid-column: span 2; }
  .dirB-grid > article:nth-child(4) { grid-column: span 4; }
}
.dirB-grid > article {
  position: relative;
  background: var(--black);
  padding: clamp(1.75rem, 3.2vw, 2.75rem);
  display: flex; flex-direction: column; gap: 12px;
  min-height: clamp(200px, 21vw, 280px);
  transition: background .22s ease;
}
/* The pop. A lift with transform would open hairline seams in a 1px-gap grid,
   so it is a gold rule wiping in from the left plus a lift in the ground. */
.dirB-grid > article::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--gold);
  transform: scaleX(0); transform-origin: left;
  transition: transform .28s var(--ease-out, cubic-bezier(0,0,.2,1));
}
.dirB-grid > article:hover { background: var(--black-2); }
.dirB-grid > article:hover::before { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .dirB-grid > article::before { transition: none; }
  /* Keep the bar put rather than sliding it. Hiding it entirely would take
     the nav away from someone who asked for less motion, not more. */
  .dirB-head { transition: none; }
  .dirB-head.is-hidden { transform: none; }
}
.dirB-grid .n { color: var(--gold); }
.dirB-grid p { color: var(--muted); max-width: 46ch; }

/* ---------- the light section ---------- */
/* The single inversion in the page. */
.dirB-lite {
  background: var(--paper); color: var(--ink);
  padding-block: var(--gap);
}
.dirB-lite h2, .dirB-lite h3 { color: var(--ink); }
.dirB-lite .body p { color: #33383D; }
.dirB-lite strong { font-weight: 600; color: var(--ink); }

.dirB-split { display: grid; gap: clamp(2rem, 5vw, 5rem); grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .dirB-split { grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); }
}

.dirB-quote {
  border-left: 3px solid var(--gold-deep);
  padding-left: clamp(1.1rem, 2.2vw, 1.75rem);
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}
.dirB-quote p {
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.15rem, 1.9vw, 1.5rem); line-height: 1.28;
  letter-spacing: -0.02em; color: var(--ink);
}
.dirB-quote cite {
  display: block; margin-top: 12px; font-style: normal;
  font-family: var(--font-mono); font-size: 11.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted-lite);
}

/* ---------- the wallpaper band ---------- */
/* Ryan liked the cropped tree behind direction A's closing band: "it's almost
   like wallpaper kind of-ish." Zoomed further here so it reads as canopy
   texture rather than as a small picture of a tree, which keeps it distinct
   from the whole silhouette used in the hero.
   The art is a background-image rather than an <img> so it can be scaled and
   positioned into the canopy without a wrapper doing transform gymnastics. */
/* Raised off the page, 2026-09-05. This band is dark and sits between two
   light sections, so Ryan asked for it to read as sitting slightly above them.

   Dark shadow here, unlike the scripture band's glow: there the band is barely
   lighter than a dark ground, here it is dark against light, so the two need
   opposite treatments to produce the same effect.

   z-index is what makes it work. Without it the following section paints over
   the shadow and only the top half survives. */
.dirB-wall {
  position: relative; overflow: hidden;
  z-index: 1;
  /* Spread 0 for the same reason as the scripture band: a negative spread
     pulled the shadow in from the left and right edges. */
  box-shadow:
    0 -18px 32px rgba(0, 0, 0, .42),
    0  18px 32px rgba(0, 0, 0, .42);
}
.dirB-wall__art {
  position: absolute; inset: 0; pointer-events: none;
  background-image: url("/assets/img/oak-white-1600.png");
  background-repeat: no-repeat;
  background-size: 210% auto;
  background-position: 50% 34%;   /* sits in the canopy, not on the trunk */
  opacity: .085;
  /* Feathered so it dissolves into the ground instead of ending on a hard
     rectangle edge. */
  -webkit-mask-image: radial-gradient(125% 105% at 50% 42%, #000 26%, transparent 76%);
          mask-image: radial-gradient(125% 105% at 50% 42%, #000 26%, transparent 76%);
}
.dirB-wall > .dirB__wrap { position: relative; z-index: 2; }

/* The same wallpaper treatment on a LIGHT ground, for the pricing band on
   /engagement/. Ryan's idea, 2026-09-05: the home page runs the white oak at
   8.5% on black, so a light section should run the BLACK oak on white.

   Three things differ from .dirB-wall__art and each is forced by the ground:
   the black asset, a lower opacity (black on near-white reads much stronger
   than white on near-black at the same value), and a slightly tighter mask so
   it stays behind the text column rather than drifting under the heading. */
.dirB-lite--art { position: relative; overflow: hidden; }
.dirB-lite--art__bg {
  position: absolute; inset: 0; pointer-events: none;
  background-image: url("/assets/img/oak-black-1600.png");
  background-repeat: no-repeat;
  background-size: 190% auto;
  background-position: 50% 38%;
  opacity: .055;
  -webkit-mask-image: radial-gradient(120% 100% at 50% 44%, #000 24%, transparent 74%);
          mask-image: radial-gradient(120% 100% at 50% 44%, #000 24%, transparent 74%);
}
.dirB-lite--art > .dirB__wrap { position: relative; z-index: 2; }

/* The blue band was tried here and rejected: Ryan's call 2026-09-02 is the
   black band, not blue. The variant and its toggle are gone rather than left
   commented out. If it is ever revisited, the finding worth keeping is that
   direction C's #1B3A57 does NOT survive on a dark ground — muted text lands
   at 4.49, under AA — and #16324C was the cut that did. */

/* ---------- stat-free proof strip ---------- */
/* Deliberately NOT counts. Counts measure what was built rather than what
   changed, and the brief rules specifics off the home page entirely. These are
   commitments, which are checkable and do not go stale. */
.dirB-commit { display: grid; gap: 1px; background: var(--hair); grid-template-columns: 1fr; }
@media (min-width: 760px) { .dirB-commit { grid-template-columns: repeat(3, 1fr); } }
.dirB-commit > div { background: var(--black); padding: clamp(1.5rem, 3vw, 2.25rem); }
.dirB-commit h3 { margin-bottom: 10px; }
.dirB-commit p { color: var(--muted); font-size: 15px; }

/* ---------- portrait ----------
   The placeholder rules that lived here are gone. Every shaped block they drew
   has been filled or cut: the portrait is a real photograph, and the diagram
   slot was removed rather than filled. See the commit for why.

   The portrait sits in the LIGHT section, not the dark one. Its own
   backdrop is a near-black charcoal, so it lands as a dark plate on the
   pale ground, which echoes the dark bands above and below rather than
   looking dropped in. Source is 928px wide, so 900 is effectively native;
   there is no real 2x to offer until a higher-resolution shot exists. */
.dirB-portrait { width: 100%; height: auto; display: block; aspect-ratio: 4/5; object-fit: cover; }

/* ---------- two-up close ---------- */
/* ---------- the page's final block ----------
   Replaced the two-up closer on 2026-09-03. That version put "Start a
   conversation" beside "See how an engagement runs", which split attention at
   the exact moment the page is asking for a decision, and spent half the
   closing on a link already sitting in the nav of every page. A page should
   end on the one thing you want someone to do. */
.dirB-final {
  padding-block: clamp(4rem, 9vw, 7.5rem);
  text-align: center;
}
.dirB-final__rule {
  width: 64px; height: 2px; background: var(--gold);
  margin: 0 auto clamp(1.75rem, 3.5vw, 2.5rem);
}
.dirB-final h2 {
  font-size: clamp(1.9rem, 4.2vw, 3.2rem);
  max-width: 18ch; margin-inline: auto;
}
.dirB-final p {
  margin: clamp(1.25rem, 2.5vw, 1.75rem) auto 0;
  max-width: min(44rem, 92%);
  font-size: clamp(1.02rem, 1.35vw, 1.18rem);
  line-height: 1.6; color: var(--muted);
}
.dirB-final .dirB-actions { justify-content: center; }

/* ---------- footer ---------- */
/* ---------- footer ----------
   Rebuilt 2026-09-03. It was three unlabelled items in a single flex row, which
   read as a stray line of text rather than a footer, and gave the page no
   ending. Now: labelled columns, then the wordmark at scale as the closing
   note, then the legal bar. The wordmark does the job the tree does elsewhere
   without repeating the tree, which by this point in a page has already
   appeared three times. */
.dirB-foot {
  padding-top: clamp(3rem, 6vw, 4.5rem);
  /* Guard. The wordmark is sized in vw against a nowrap string, so a font
     substitution or a metric change could push it past the viewport and give
     the whole document a horizontal scrollbar. Clipping here means the worst
     case is a cropped letter rather than a broken page. */
  overflow-x: clip;
}

.dirB-foot__cols {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: 1fr;
}
@media (min-width: 700px) {
  .dirB-foot__cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.dirB-foot__cols h2 {
  font-family: var(--font-mono); font-weight: 500;
  font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 1rem;
}
.dirB-foot__cols ul { display: grid; gap: .55rem; }
.dirB-foot__cols li, .dirB-foot__cols p { font-size: 15px; color: var(--muted); }
.dirB-foot a {
  color: var(--paper); text-decoration: none;
  border-bottom: 1px solid transparent; transition: border-color .15s;
}
.dirB-foot a:hover { border-bottom-color: var(--paper); }

/* The wordmark, set to fill the measure. Low contrast on purpose: it is the
   page signing off, not another heading competing for attention. */
.dirB-foot__mark {
  /* Sits last, and outside the text column: at 12.2vw it filled that column to
     the pixel at every width, so the only way to grow it is to let it run to
     the viewport edges instead. Padding rather than margin at the foot so the
     descender on the g has somewhere to go, since line-height 0.9 crops it
     against a hard edge. */
  margin-top: clamp(2.25rem, 4.5vw, 3.5rem);
  padding-inline: clamp(.5rem, 1vw, 1rem);
  padding-bottom: clamp(1rem, 2vw, 1.75rem);
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(2.8rem, 12.4vw, 15.5rem);
  line-height: .9; letter-spacing: -.05em;
  text-align: center;
  /* letter-spacing applies after the LAST character as well, so with a negative
     value the text box ends short of where the glyphs do and centering leaves
     the word sitting left: measured 55px against 70px at 1440. .05em cancels
     that trailing gap; the rest closes the residual left/right side bearings on
     the O and the g. Expressed in em so it holds at every size. */
  text-indent: .067em;
  color: rgba(244, 245, 246, .085);
  user-select: none; white-space: nowrap;
}

/* The copyright sits under the contact details rather than in a bar of its
   own, so the footer carries three columns and a wordmark instead of three
   columns, a bar and a wordmark. */
.dirB-foot__legal { margin-top: 1.5rem; font-size: 13px; color: var(--muted); }


/* ==========================================================================
   Print. A dark page otherwise comes out as a black rectangle or gets its
   backgrounds stripped by the browser. One of the few real costs of going
   dark, and cheap to answer.
   ========================================================================== */
@media print {
  .dirB, .dirB-lite, .dirB-wall { background:#fff !important; color:#111 !important; }
  .dirB h1, .dirB h2, .dirB h3, .dirB .mono, .dirB p, .dirB a { color:#111 !important; }
  .dirB-hero { min-height:auto !important; }
  .dirB-hero__oak, .dirB-wall__art, .dirswitch { display:none !important; }
  .dirB-grid, .dirB-commit { background:#ccc !important; }
  .dirB-grid > article, .dirB-commit > div { background:#fff !important; }
}

/* ==========================================================================
   Interior pages

   The home page opens on a full-viewport hero. Interior pages must not: a
   reader who clicked "What we do" wants the answer, not another curtain. They
   open on a compact masthead instead, sized so the first real content is
   visible without scrolling on a laptop.
   ========================================================================== */

/* The header is fixed over the home hero. Interior pages have no hero to sit
   over, so it holds its own space in the flow instead. Sticky rather than
   static: it still needs to hide and return on scroll, and sticky occupies
   flow space the way static did, so nothing below it shifts. The class name
   is kept because four templates set it. */
.dirB-head--static { position: sticky; top: 0; }

/* NO border-bottom. Removed 2026-09-05 at Ryan's call, along with the rules on
   .dirB-end, .dirB-final and .dirB-foot. His note: the edge-to-edge lines were
   "chopping up the page and sectioning it, partitioning it off" and made it
   read as less flowy. The IN-COLUMN rules stay: .dirB-cap, .dirB-stage and
   .dirB-under all keep theirs, because those separate peers inside one idea
   rather than walling off one idea from the next. The distinction is width,
   not weight. */
.dirB-masthead {
  padding-block: clamp(2.5rem, 6vw, 4.5rem) clamp(2.5rem, 5vw, 4rem);
}
/* No max-width, 2026-09-05, Ryan's call. It was 17ch, which at this size is
   about 657px of a 1256px column, so every interior title broke early and sat
   in the left half of the page while the body text below it ran the full
   width. His note on the framework title: "way too condensed just to the left
   ... we can really extend this one out."

   Unconstrained, the title now ends where the capability body text ends. The
   line count falls out of the words rather than out of an arbitrary cap:
   /engagement/ and /about/ land on one line, /approach/ and /framework/ on
   two, which is what he asked for.

   The 17ch cap is why .dirB-masthead--full existed, to opt the About page out.
   That modifier is now redundant but is left in place: it is on the About page
   markup and removing it would be a change with no visible effect. */
.dirB-masthead h1 {
  font-size: clamp(2.3rem, 5.4vw, 4.2rem);
  margin-top: clamp(1rem, 2vw, 1.5rem);
}
/* Opt in where a headline is short enough to hold one line and reads better
   for it. The 17ch default exists to stop long headlines running the full
   1400px measure; a short one does not need protecting from that. Below the
   breakpoint it wraps naturally, because it has to. */
.dirB-masthead--full h1 { max-width: none; }

/* About only. Every other interior page runs the masthead into another DARK
   section, so the transition is invisible. About runs it straight into the
   white bio panel, and with a one-line headline the dark band was short enough
   that it read as a stripe rather than a masthead. Ryan: "it, like, quickly
   runs into the white. It just seems odd."

   Two things fix it. More room below the lede, so the band has height of its
   own. And a gradient in the last 140px of it, so the dark resolves into the
   light instead of meeting it on a hard line.

   The gradient sits on a pseudo-element rather than the section background,
   because the section has no background of its own: it inherits the page
   ground, and painting one here would break the hero on the pages that share
   this class. */
.dirB-masthead--full {
  position: relative;
  padding-bottom: clamp(4rem, 9vw, 7.5rem);
}
.dirB-masthead--full::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  /* Kept just under the section's own bottom padding at every width. At 375px
     the padding resolves to 64px, and a 70px gradient overlapped the lede by
     6px. The z-index below meant it never showed, but a decorative layer
     reaching into live text is a bug waiting for a different font to make it
     visible. */
  height: clamp(52px, 8vw, 140px);
  background: linear-gradient(to bottom, transparent, var(--paper));
  pointer-events: none;
}
.dirB-masthead--full > .dirB__wrap { position: relative; z-index: 2; }
/* The subtitle tracks the title, 2026-09-05, Ryan's call. Both now run the
   full content column, so the masthead reads as one block with one right edge
   instead of a wide headline over a narrower paragraph.

   The trade, stated because it is real: at this width the lede runs well past
   the 65-75 character measure that is comfortable for continuous reading. It
   holds here because these ledes are one or two sentences that get scanned,
   not paragraphs that get read. Do NOT copy this to running body text. */
.dirB-masthead .lede {
  margin-top: clamp(1.25rem, 2.5vw, 1.75rem);
  font-size: clamp(1.05rem, 1.4vw, 1.25rem);
  max-width: none;
}

/* A capability block: the category, what it means, and the named pieces of
   work that sit under it. The taxonomy research is explicit that the top layer
   carries every audience and the layer beneath it carries the proof. */
.dirB-cap {
  display: grid; gap: clamp(1rem, 2.5vw, 2.5rem);
  grid-template-columns: 1fr;
  padding-block: clamp(2.25rem, 4.5vw, 3.5rem);
  border-bottom: 1px solid var(--hair);
}
@media (min-width: 900px) {
  /* The heading column is held at a fixed measure rather than 1fr. As 1fr it
     took half the row while the headings only filled 290px of it, so the gap
     read as an accident. */
  /* Back down to 19rem on 2026-09-05. Titles carrying an ampersand are now
     stacked over three lines, so the widest single line in this column is the
     longest title WITHOUT one, "Operational diagnosis" at ~304px. The 23rem
     that a one-line "Process & policy design" needed is no longer required,
     and the difference goes back to the body column beside it.

     The number column stays at 3rem. It holds two digits and 4.5rem was always
     more than it needed. */
  .dirB-cap { grid-template-columns: 3rem minmax(0, 19rem) minmax(0, 1fr); align-items: start; }
}
.dirB-cap__n { color: var(--gold); }
/* No ch measure here. ch is font-size relative, and at this size 18ch is only
   about 290px, which broke "Process and policy design" across two lines while
   630px of column sat empty beside it. The column width is the constraint. */
.dirB-cap h2 { font-size: clamp(1.5rem, 2.5vw, 2rem); }
 /* :where() wraps the SCOPE, not the element. `.dirB-cap :where(p)` would still
    be (0,1,0) from the class and would tie with `.dirB-cap__n`, losing on
    source order. `:where(.dirB-cap) p` is (0,0,1) and loses to it properly. As `.dirB-cap p` it was (0,1,1) and silently
    overrode `.dirB-cap__n` at (0,1,0), which meant the gold on the 01-04
    numbers had never once rendered. Found by reading computed styles in a
    browser, 2026-09-05; reading the stylesheet would never have shown it. */
:where(.dirB-cap) p { color: var(--muted); }

/* Stacked category titles, 2026-09-05, Ryan's call. Anything with an ampersand
   breaks over three lines with the ampersand alone in the middle, and the whole
   block centers in its column. Titles without one stay on a single line and
   center with them.

   The break is in the MARKUP rather than in CSS because it is a typographic
   decision, not a consequence of the available width: it should hold at every
   viewport and never re-flow into two lines on a wider screen.

   The ampersand is muted rather than gold. The 01-04 number in the column
   beside it is already gold, and two gold marks in one row makes the eye
   choose between them. Muted lets it act as a hinge between the two words
   instead of competing with the number. */
.dirB-stack { text-align: center; }
.dirB-stack > span { display: block; }
.dirB-stack__amp {
  color: var(--muted);
  font-weight: 400;
  /* Optically smaller. At the heading weight a lone ampersand on its own line
     outweighs the words it is joining. */
  font-size: .78em;
  line-height: 1.35;
}

.dirB-under { margin-top: 1.25rem; }
.dirB-under li {
  padding-block: 9px; border-top: 1px solid var(--hair);
  font-size: 15px; color: var(--paper);
}
.dirB-under li:first-child { border-top: 0; }

/* Maturity levels, on /framework/. Reuses the .dirB-cap grid, so the six
   dimensions line up with the four capabilities on /approach/ and the two
   pages read as one system.

   The pair is a state change, so the two halves are deliberately not
   symmetrical: the starting state sits in --muted behind a --hair rule and the
   target sits in --paper behind gold. That way the direction of travel is
   legible before a word is read.

   --hair is correct on both. These are decorative rules, not the boundary of a
   UI component, so 1.4.11 does not apply and --control-edge would be too loud
   at this density. */
.dirB-lv {
  padding-left: 1rem;
  border-left: 2px solid var(--hair);
  margin-bottom: 1.35rem;
}
.dirB-lv:last-child { margin-bottom: 0; }
.dirB-lv .mono {
  display: block;
  margin-bottom: .3rem;
  color: var(--muted);
}
.dirB-lv--target { border-left-color: var(--gold); }
/* Gold reaches 8.05 on this ground, so the label is allowed to carry it here.
   It would not be on a light ground: see the palette notes at the top. */
.dirB-lv--target .mono { color: var(--gold); }
.dirB-cap .dirB-lv--target { color: var(--paper); }

/* Numbered stages, for the engagement arc. Distinct from the capability list
   on purpose: this one IS a sequence and is allowed to look like one. */
.dirB-stage {
  display: grid; gap: clamp(.75rem, 2vw, 2.5rem);
  grid-template-columns: 1fr;
  padding-block: clamp(2rem, 4vw, 3rem);
  border-bottom: 1px solid var(--hair);
  position: relative;
}
@media (min-width: 900px) {
  .dirB-stage { grid-template-columns: 8.5rem minmax(0, 1fr); }
}
/* Word labels, not numerals. The capability list already uses .01-.04, and two
   different numbered fours on one site (both ending in "handover") is exactly
   the collision this wording avoids. */
/* These were ALREADY gold when Ryan asked whether they could be gold, so the
   change that answers him is presence rather than hue: up from 11.5px, more
   tracking, and a short gold rule above the word so the label announces the
   stage instead of captioning it.

   Still word labels rather than numerals. /approach/ owns .01-.04 and two
   numbered systems on one site invite the reader to think the four areas and
   the four stages are the same four things. They are not. */
.dirB-stage__n {
  font-family: var(--font-mono);
  font-size: 13px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold); padding-top: .55rem; white-space: nowrap;
}
.dirB-stage__n::before {
  content: ""; display: block;
  width: 1.75rem; height: 2px; background: var(--gold);
  margin-bottom: .7rem;
}
/* The measure lives on the CONTAINER, not on the heading and the paragraph
   separately. Both then resolve to exactly the same right edge, which is what
   Ryan asked for: the stage title running out to where the text below it ends.
   Setting it twice in ch would not have aligned, because ch resolves against
   each element's own font-size and the heading is nearly twice the body size.

   The global `.dirB h2 { max-width: 21ch }` was the actual cause of the wrap:
   it capped the stage titles at 377px when the longest one needs 479px and the
   column had 1107px free. Overridden here rather than loosened globally, since
   21ch is doing real work on the wider headings elsewhere. */
.dirB-stage > div { max-width: 62ch; }
.dirB-stage h2 { font-size: clamp(1.4rem, 2.4vw, 1.95rem); margin-bottom: .75rem; max-width: none; }
 /* Same trap as .dirB-cap above, and this is the one Ryan spotted from the
    outside: he asked whether the stage labels could be gold. They already said
    gold and had never been gold, because `.dirB-stage p` outranked
    `.dirB-stage__n`. */
:where(.dirB-stage) p { color: var(--muted); }
.dirB-stage p + p { margin-top: .9em; }

/* A quiet callout for things that need to be said plainly: how pricing works,
   what is deliberately not claimed. */
.dirB-note {
  border-left: 2px solid var(--gold);
  padding: clamp(1rem, 2vw, 1.5rem) clamp(1.1rem, 2.2vw, 1.75rem);
  background: var(--black-2);
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}
.dirB-lite .dirB-note { background: #E9EBED; border-left-color: var(--gold-deep); }
.dirB-note h3 { font-size: 1.05rem; margin-bottom: .5rem; }
.dirB-note p { color: var(--muted); font-size: 15px; }
.dirB-note p + p { margin-top: .75em; }
.dirB-lite .dirB-note p { color: var(--muted-lite); }

/* Closing call to action on interior pages. */
/* An onward link. Steel measures 5.08 on the dark ground and 6.63 on the light
   one, so it clears AA on both and needs no per-ground variant. */
.dirB-more {
  display: inline-flex; align-items: center; gap: 10px;
  margin-top: 1.5rem;
  color: var(--steel); text-decoration: none; font-weight: 500;
  border-bottom: 1px solid rgba(74,136,181,.35);
}
.dirB-lite .dirB-more { color: #2B5B7E; border-bottom-color: rgba(43,91,126,.35); }
.dirB-more span { transition: transform .2s ease; }
.dirB-more:hover span { transform: translateX(5px); }
@media (prefers-reduced-motion: reduce) { .dirB-more span { transition: none; } }

.dirB-end {
  padding-block: clamp(3.5rem, 7vw, 6rem);
  display: grid; gap: clamp(1.25rem, 3vw, 2.5rem); grid-template-columns: 1fr;
}
/* Centered, 2026-09-05. Two problems, both Ryan's observation that it "seems
   too far to the left".

   First it was genuinely misaligned. .dirB-end has no wrap of its own, so each
   .dirB__wrap grid item applied its padding inside a half-width column and the
   heading landed at x=72 while every other element on the page started at
   x=85. Thirteen pixels adrift of everything, on every interior page.

   Second, the split layout put the heading in one column and the invitation in
   the other, which reads as two things at the moment the page is asking for
   one. The home page already closes on a single centered call to action and has
   done since 79a7eec. The interior pages now match it. */
.dirB-end { text-align: center; }
.dirB-end h2 { margin-inline: auto; }
.dirB-end .lede { margin-inline: auto; }
.dirB-end .dirB-actions { justify-content: center; }
/* The wrappers must fill their grid column. .dirB__wrap carries margin: 0 auto,
   and a grid item with auto side margins does NOT stretch: the margins absorb
   the free space and center it at its content width instead. The heading is
   capped at 15ch, so its wrapper shrank to 274px and centered while the wrapper
   below it filled 375px, leaving the heading sitting 50px in from everything
   else. Measured on the About page at 390px wide. */
.dirB-end > .dirB__wrap { width: 100%; }
 /* 15ch put the engagement closer on three lines. Ryan wanted it wider, with
    "Start with the conversation" landing on the first line. 30ch does that and
    keeps the longer closers to two. */
.dirB-end h2 { font-size: clamp(1.7rem, 3.2vw, 2.6rem); max-width: 30ch; }

/* ==========================================================================
   Scripture band

   Isaiah 61:3 set apart rather than paraphrased inside a paragraph. The oak
   engraving sits behind it because the verse is literally about oaks, which
   ties the verse, the firm's name and the mark into one moment instead of
   three separate references.

   Measured on the raised panel #14171A: paper 16.48, gold 7.44, muted 6.89.
   All clear AA, and gold clears AAA, so the emphasis may be gold here. On a
   LIGHT ground it could not be, which is the standing rule.
   ========================================================================== */
/* The hard rules above and below became a GLOW on 2026-09-05, Ryan's call.
   Two 1px hairlines read as the band being fenced off; light spilling above and
   below reads as the band being lit from behind, which is the right feeling for
   the verse the firm is named out of.

   Light rather than dark because the band sits on the page ground and is only
   a shade lighter than it. A dark shadow would be invisible. The spread is
   negative so the glow stays close to the edge instead of hazing the whole
   section. */
.dirB-scripture {
  position: relative; overflow: hidden;
  background: var(--black-2);
  padding-block: clamp(3.5rem, 8vw, 6.5rem);
  text-align: center;
  z-index: 1;
  /* Spread 0, not negative. A negative spread shrinks the shadow rect on ALL
     four sides, so the glow stopped 26px short of the left and right edges and
     read as ending early. At 0 it spans the full width and runs off both sides.
     Alpha comes down to compensate for the larger area. */
  box-shadow:
    0 -20px 34px rgba(244, 245, 246, .11),
    0  20px 34px rgba(244, 245, 246, .11);
  /* A near-black seam so the band has a defined edge INSIDE the glow. Without
     it the lit edge has nothing to be an edge of. */
  border-block: 1px solid rgba(0, 0, 0, .55);
}
.dirB-scripture__art {
  position: absolute; inset: 0; pointer-events: none;
  background-image: url("/assets/img/oak-white-1600.png");
  background-repeat: no-repeat;
  background-size: 165% auto;
  background-position: 50% 42%;
  opacity: .07;
  -webkit-mask-image: radial-gradient(115% 100% at 50% 45%, #000 22%, transparent 72%);
          mask-image: radial-gradient(115% 100% at 50% 45%, #000 22%, transparent 72%);
}
.dirB-scripture > .dirB__wrap { position: relative; z-index: 2; }

.dirB-scripture blockquote {
  margin: 0 auto;
  /* Deliberately not a ch measure. ch scales with font size, and at this
     display size it produces a line far longer than it looks in the rule. */
  max-width: min(44rem, 92%);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.35rem, 2.9vw, 2.35rem);
  line-height: 1.32;
  letter-spacing: -.02em;
  color: var(--paper);
}
/* The two phrases his story turns on. */
.dirB-scripture blockquote em {
  font-style: normal;
  color: var(--gold);
}
.dirB-scripture cite {
  display: block; margin-top: clamp(1.5rem, 3vw, 2.25rem);
  font-family: var(--font-mono); font-style: normal;
  font-size: 11.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold);
}
/* A hairline pair above and below the reference, the printer's device from the
   engraved direction, kept as the one ornament on the site. */
.dirB-scripture cite::before,
.dirB-scripture cite::after {
  content: ""; display: inline-block; vertical-align: middle;
  width: clamp(1.5rem, 4vw, 3rem); height: 1px;
  background: currentColor; opacity: .5;
}
.dirB-scripture cite::before { margin-right: 1rem; }
.dirB-scripture cite::after  { margin-left: 1rem; }

.dirB-scripture__after {
  position: relative; z-index: 2;
  max-width: min(40rem, 92%); margin: clamp(2.25rem, 4.5vw, 3.25rem) auto 0;
  color: var(--muted); font-size: 1.02rem; line-height: 1.6;
}
