/* ============================================================================
   HENRY COLLINS — THEME MODULE
   ----------------------------------------------------------------------------
   Single source of truth for the whole site. Nothing in the PHP templates
   carries a style="" attribute any more; every visual decision lives here.

   Structure
     1  Tokens ................ colour, type, spacing, motion
     2  Base .................. reset, document defaults, a11y
     3  Layout ................ .band / .wrap / .grid
     4  Typography ............ .eyebrow / .h-* / .lede / .prose / .meta
     5  Controls .............. .btn / .link-arrow / .fld
     6  Components ............ header, footer, hero, cta, card, media, …
     7  Page modules .......... accordion, slider, pillars, programs, events
     8  Motion ................ reveal, hover-lift, keyframes
     9  Responsive ............ one place, class-based

   THEMING A SECTION
     Put a band class on <section> and everything inside inherits the palette:

       <section class="band band--purple">        dark purple, gold eyebrow
       <section class="band band--paper2 rule-b"> light, hairline bottom rule

     Light sections pick their accent (card rules, numerals, kickers) with an
     accent class — .accent--purple, .accent--green, … — so one attribute
     re-themes an entire section.
   ============================================================================ */


/* ============================================================================
   1  TOKENS
   ============================================================================ */
:root {
  /* -- Surfaces ------------------------------------------------------------ */
  --paper:        #F3EDE1;
   /* --paper:        #FAF8F3; */
  --paper2:       #FBF7EE;
  /* --paper2:       #FAF8F3; */
  --ink:          #1B1712;
  /* The chrome: announcement bar, on-this-page bar, footer. One near-black for
     all three, so the frame around the page reads as a single surface no matter
     which coloured banner sits between them. Darker than --ink, which stays the
     body-text colour. */
  --chrome:       #11120F;
  --sub:          #6E6558;
  --line:         #E2D8C6;
  --muted:        #CBBFA9;
  --muted-2:      #B5AB99;
  --prose:        #2C2822;
  --placeholder:  #A89E8E;

  /* -- Brand --------------------------------------------------------------- */
  --blue:         #123742;
  --blueHi:       #1C5568;
  --purple:       #5E4C7A;
  /* The Foundation green runs as a four-step scale rather than a single hue, so
     the Outreach page can build light, mid and dark sections out of one colour
     instead of borrowing ink / purple / blue for its dark bands. Gold stays the
     only warm accent on top of it. */
  --greenInk:     #16281F;
  --greenDeep:    #2F5540;
  --green:        #3F6B52;
  --greenBright:  #4E8467;
  --bronze:       #7A5230;
  --tan:          #B98A38;
  --rust:         #8B3A1F;

  /* The Writing page's paper, promoted from that page's scope so the homepage
     teaser and the About next-row can use it. A teaser band is a door into a
     page, so it has to be that page's colour.

     Unlike the other three service colours this one is LIGHT -- the Writing
     page is paper stock, not a dark ground -- so the band it drives carries
     ink rather than paper text. --olive-ink is that colour as text, for the
     places a service colour has to be readable on paper instead of behind it. */
  --olive:        #C7CBA8;
  --olive-ink:    #565211;

  /* -- Gold, in three jobs --------------------------------------------------
     One hue, three tokens, because a single gold cannot do all three jobs and
     stay legible. Measured against every band it is used on:

       --gold       #D9AC5B  a FILL, and large display type only. It clears the
                             3:1 large-text bar on the dark bands but falls to
                             2.9:1 on mid-green -- never set small text in it.
       --gold-text  #F2DDB0  gold as SMALL text on any dark band. Worst case
                             4.6:1, so it clears AA everywhere it can appear.
       --gold-deep  #8A6413  gold as text on PAPER, where the other two vanish
                             (1.8:1). Was scoped to the About page; global now,
                             because the problem it solves was never local.

     This mirrors the split the Writing page already makes between --w-olive as
     a fill and --w-olive-deep as text. Same rule, applied site-wide. */
  --gold:         #D9AC5B;
  --gold-text:    #F2DDB0;
  --gold-deep:    #8A6413;

  /* -- Night palette: the Applications page ---------------------------------
     Software gets its own surface: a cool slate rather than near-black, so
     screenshots of real UI sit on it without the page reading as a void. The
     three steps are deliberately far apart — the hero and timeline take the
     deepest tone, the application grid sits a full step lighter so the cards
     have something to lift off, and the screenshot chrome is lighter again. */
  --night:        #0E212B;
  --night-2:      #16323E;
  --night-3:      #1E4453;
  --tealBright:   #2FBFA3;
  --on-night:     #EAF3F1;
  --fade-night:   rgba(234,243,241,.8);
  --hair-night:   rgba(140,215,200,.24);

  /* -- Foregrounds used on dark bands -------------------------------------- */
  --on-blue:      #FBF7EE;
  --on-green:     #EAF1EC;
  --on-purple:    #F0ECF4;
  --on-bronze:    #F4EEE4;

  /* -- Translucent ink/paper (dark-band body copy, hairlines) -------------- */
  --fade-strong:  rgba(234,241,236,.88);
  --fade:         rgba(234,241,236,.82);
  --fade-soft:    rgba(243,237,225,.72);
  --fade-weak:    rgba(243,237,225,.55);
  --hair-light:   rgba(255,255,255,.20);
  --hair-lighter: rgba(255,255,255,.12);

  /* -- Type ---------------------------------------------------------------- */
  --font-sans: 'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* -- The label scale ------------------------------------------------------
     Every uppercase mono label on the site is one of three sizes. The tracking
     is bound to the size rather than chosen per component: small type needs
     more of it to stay readable in caps, large type needs less or it falls
     apart into separate letters. 11px is the floor -- below that the mono face
     stops being legible at a glance, which is the only reason a label exists. */
  --label:      11.5px;   /* the default: section kickers, card meta         */
  --label-lg:   12px;     /* the hero kicker, and it alone                   */
  --label-sm:   11px;     /* dense rows -- tabs, chips, timeline years       */
  --track:      .14em;
  --track-lg:   .16em;
  --track-sm:   .1em;

  /* -- The display scale ----------------------------------------------------
     Six steps, each a clamp so it fluid-scales with the viewport. Headings pick
     a STEP; they never write a size. A page then chooses the face and the case
     -- serif on Writing and About, sans everywhere else -- which is what makes
     three identities read as one family: same rhythm, different voice.

     Tracking tightens as the size grows, because large type looks loose at the
     tracking that suits small type. It is bound to the step for the same reason
     the label tracking is bound to the label size. */
  --step-6: clamp(40px, 7.8vw, 78px);   /* hero -- one per page               */
  --step-5: clamp(34px, 6.4vw, 60px);   /* closing call to action             */
  --step-4: clamp(28px, 4.6vw, 44px);   /* section heading -- the workhorse   */
  --step-3: clamp(26px, 3.8vw, 36px);   /* subsection, large card             */
  --step-2: 24px;                       /* card heading                       */
  --step-1: 20px;                       /* dense card, list heading           */

  --tight-6: -.035em;
  --tight-5: -.03em;
  --tight-4: -.025em;
  --tight-3: -.02em;
  --tight-2: -.01em;

  /* Deliberately OUTSIDE this scale, and they should stay outside it:
       · display numerals (.fact__figure, .hstat__num, .erapanel__year) are
         figures, not headings -- they are sized against the block they label;
       · pull quotes (.wr-quote__text, .ab-quote, .ab-slide__quote) and the
         About standfirst set a reading measure, not a heading rank;
       · .wr-hero__title is the Writing page's one signature -- the book-jacket
         line it is built around.
     Everything that ranks a section uses a step above. */

  /* -- Layout -------------------------------------------------------------- */
  --wrap-max:  1280px;
  /* The narrower editorial measure. Pages whose job is sustained reading
     (About, and the Writing article column) opt into this deliberately via
     .wrap--reading rather than inventing a third width of their own. */
  --wrap-read: 1180px;
  --wrap-x:    44px;
  --wrap-y:    76px;
  --gap:       24px;
  --gap-lg:    56px;

  /* -- Card padding ---------------------------------------------------------
     The inside of a boxed object, in three sizes chosen by the object's WIDTH
     rather than by whoever wrote it. Peer cards had drifted to 22, 24, 26, 28,
     30, 32 and 44px -- on the homepage a testimonial (32) sat beside a card
     (28) and an offerings panel (44), so three boxes on one screen indented
     their text by three different amounts.

       --pad-card-sm   small cards in a 3-up grid: blog, activity, testimonial
       --pad-card      the default box, and what .card uses
       --pad-card-lg   full-width panels that carry a heading and a column

     A component names the STEP, never a number, so the whole site re-spaces
     from three lines here. */
  --pad-card-sm: 22px;
  --pad-card:    28px;
  --pad-card-lg: 44px;

  /* -- Gap ------------------------------------------------------------------
     The space BETWEEN sibling items, on the same principle as the card scale.
     --gap and --gap-lg already existed and are kept; these fill the two steps
     under them that components were writing by hand as 10/11/12/13/14/15/16px
     and 26/28/30/32px. Peer rows sitting one above the other had picked
     different ones, which is what made a column of them look unruled. */
  --gap-xs:    12px;   /* icon to label, chip rows, dense list items          */
  --gap-sm:    16px;   /* card internals, tab rows                            */
  --gap-md:    32px;   /* between blocks inside a section                     */

  /* -- Radii --------------------------------------------------------------- */
  /* The raw steps. Components never name these directly -- they name a ROLE
     below, so a page can restate its whole shape language in three lines. */
  --r-xs:   2px;
  --r-sm:   5px;
  --r-md:   6px;
  --r-lg:  10px;
  --r-pill: 40px;

  /* -- Shape roles ----------------------------------------------------------
     Three roles cover every cornered object on the site:

       --radius-surface   cards, panels, media boxes, tabs
       --radius-control   buttons, inputs, selects
       --radius-pill      chips, badges, filters

     Peer objects had drifted to four different radii -- cards alone ran 5px,
     6px and 10px on the same page -- because each component picked its own
     step. Naming the role instead means the shape is a PAGE decision, and a
     page changes its whole language by resetting three variables.

     Circles (avatars, dots, icon wells) stay 50% and are not a role: they are
     round because they are round, not because a page said so. */
  --radius-surface: var(--r-md);
  --radius-control: var(--r-xs);
  --radius-pill:    var(--r-pill);

  /* -- Accent stripe --------------------------------------------------------
     The slab of colour a card wears on one edge to claim a section's accent.
     It had drifted to 3px, 4px, 5px and 6px on components that sit side by
     side -- a testimonial at 4px next to a pillar panel at 5px reads as a
     mistake, not a hierarchy, because nothing distinguishes the two ranks.
     One width, named once: a card either wears the stripe or it does not. */
  --stripe: 4px;

  /* -- Control sizing -------------------------------------------------------
     Round carousel/slider controls are one size everywhere. The About page ran
     46px squares against the shared 48px circles, so the same "next quote"
     button changed shape between pages. */
  --control-size: 48px;

  /* -- Pagination dot -------------------------------------------------------
     Every slider on the site says "you are here" the same way: a small dot that
     stretches into a pill on the active step. It had been a 9px->26px pill on
     the home and archive sliders, a plain 9px circle on Writing and a 34x5px
     bar on About -- three different marks for one meaning, on three pages a
     reader moves between. Colour still comes from the page. */
  --dot:        9px;
  --dot-active: 26px;

  /* -- Motion -------------------------------------------------------------- */
  --ease:   cubic-bezier(.16,.84,.44,1);
  --t-fast: .25s;
  --t-base: .3s;
  --t-slow: .45s;

  /* -- Elevation ----------------------------------------------------------- */
  --shadow-card: 0 16px 34px rgba(18,55,66,.13);
  --shadow-lift: 0 18px 40px rgba(18,55,66,.16);
  --shadow-soft: 0 12px 28px rgba(18,55,66,.10);

  /* -- Contextual: reassigned by band/accent classes ----------------------- */
  --accent:       var(--blue);
  --band-bg:      transparent;
  --band-fg:      var(--ink);
  --band-eyebrow: var(--sub);
  --band-hair:    var(--line);
}


/* ============================================================================
   2  BASE
   ============================================================================ */
*, *::before, *::after { box-sizing: border-box; }

/* Must beat the display value on components like .btn */
[hidden] { display: none !important; }

html, body { margin: 0; }

/* In-page anchors ease rather than jump; the reduced-motion block turns it off */
html { scroll-behavior: smooth; }
/* Nothing on the site is meant to scroll the page sideways. Wide things that
   genuinely need it (the pagenav row, code blocks) scroll inside their own
   container; this stops one long unbroken string from doing it to the page. */
html, body { overflow-x: clip; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; }

a { color: inherit; transition: color var(--t-fast) var(--ease); }
/* Hover shifts toward the band's accent rather than fading the link out. A
   global opacity drop lowered contrast at the exact moment the user was
   confirming their target, and it was dimmest on the dark bands where the text
   could least afford it. Components that set their own hover still win. */
a:hover { color: var(--accent); }

/* One focus ring for the entire site. Pages and bands never restate this rule;
   they set --focus and the ring follows. Defaults to currentColor so any new
   surface is legible before anyone has thought about it. */
:focus-visible {
  outline: 3px solid var(--focus, currentColor);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--ink); color: var(--paper2);
  padding: 12px 20px;
  font-family: var(--font-mono); font-size: 13px;
}
.skip-link:focus { left: 8px; top: 8px; }

/* Screen-reader-only utility */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* ============================================================================
   3  LAYOUT
   ============================================================================ */

/* -- Band: the themed full-bleed section shell ----------------------------- */
/* A band's accent is chosen to contrast with its own ground -- a brand colour on
   the light bands, gold or bright teal on the dark ones -- which makes it the
   right focus ring in both cases. Setting it here means every band gets a
   legible ring for free and no component ever restyles focus. */
.band {
  background: var(--band-bg);
  color: var(--band-fg);
  --focus: var(--accent);
}

.band--paper      { --band-bg: var(--paper);  }
.band--paper2     { --band-bg: var(--paper2); }

.band--blue       { --band-bg: var(--blue);      --band-fg: var(--on-blue);   --accent: var(--gold); --band-eyebrow: var(--gold); --band-hair: var(--hair-light); }
.band--blue-hi    { --band-bg: var(--blueHi);    --band-fg: var(--on-blue);   --accent: var(--gold-text); --band-eyebrow: var(--gold-text); --band-hair: var(--hair-light); }
.band--purple     { --band-bg: var(--purple);    --band-fg: var(--on-purple); --accent: var(--gold-text); --band-eyebrow: var(--gold-text); --band-hair: var(--hair-light); }
.band--bronze     { --band-bg: var(--bronze);    --band-fg: var(--on-bronze); --accent: var(--gold-text); --band-eyebrow: var(--gold-text); --band-hair: var(--hair-light); }
/* The teaser takes the light paper olive -- the same --olive that is the
   Writing page's own ground, so the banner reads as a door into that page --
   and carries white type and a white button, matching the other three teasers.

   Contrast note, recorded deliberately: white on this ground is 1.68:1, where
   the other teaser bands run 6.3:1 (green) to 15.1:1 (night) because those are
   dark grounds and this one is light. The white button inverts to a white fill
   with a #C7CBA8 label, also 1.68:1. This is a specified design choice, not an
   oversight -- do not "fix" it by darkening the olive or inking the type
   without checking first. */
.band--olive      { --band-bg: var(--olive);     --band-fg: #FFFFFF;          --accent: #FFFFFF; --band-eyebrow: #FFFFFF; --band-hair: rgba(255,255,255,.20); }
.band--green      { --band-bg: var(--green);     --band-fg: var(--on-green);  --accent: var(--gold-text); --band-eyebrow: var(--gold-text); --band-hair: var(--hair-light); }
.band--green-deep { --band-bg: var(--greenDeep); --band-fg: var(--on-green);  --accent: var(--gold-text); --band-eyebrow: var(--gold-text); --band-hair: var(--hair-light); }
/* The darkest step — what .band--ink would otherwise be used for on a page
   whose colour is green. Same weight on the page, none of the hue clash. */
.band--green-ink  { --band-bg: var(--greenInk);  --band-fg: var(--on-green);  --accent: var(--gold); --band-eyebrow: var(--gold); --band-hair: var(--hair-light); }
/* The dark section band is a SURFACE, so it takes the chrome near-black rather
   than the body-text ink. On Outreach these bands sit directly above the footer
   and below the announcement strip -- on --ink they were a visibly different
   near-black from the frame touching them, which read as a printing fault
   rather than a decision. */
.band--ink        { --band-bg: var(--chrome);    --band-fg: var(--paper2);    --accent: var(--gold); --band-eyebrow: var(--gold); --band-hair: var(--hair-light); }
.band--night      { --band-bg: var(--night);     --band-fg: var(--on-night);  --accent: var(--tealBright); --band-eyebrow: var(--tealBright); --band-hair: var(--hair-night); }
.band--night-2    { --band-bg: var(--night-2);   --band-fg: var(--on-night);  --accent: var(--tealBright); --band-eyebrow: var(--tealBright); --band-hair: var(--hair-night); }

/* Accent-only themes for light sections */
.accent--blue    { --accent: var(--blue);    }
.accent--blue-hi { --accent: var(--blueHi);  }
.accent--purple  { --accent: var(--purple);  }
.accent--green   { --accent: var(--green);   }
.accent--green-deep { --accent: var(--greenDeep); }
/* Decorative only — at 3.7:1 on paper this is fine for a rule, a badge or a
   bullet, but it must not be asked to carry small text. */
.accent--green-bright { --accent: var(--greenBright); }
.accent--bronze  { --accent: var(--bronze);  }
.accent--gold    { --accent: var(--gold);    }
.accent--teal    { --accent: #14806D;         }  /* the night teal, darkened for paper */

/* Section rules — hairline, not a hard divider. Follows --band-hair so the line
   stays soft on paper bands and light-on-dark inside coloured bands. */
.rule-b { border-bottom: 1px solid var(--band-hair); }
.rule-t { border-top: 1px solid var(--band-hair); }

/* -- Wrap: the 1280px content column --------------------------------------- */
.wrap {
  max-width: var(--wrap-max);
  margin: 0 auto;
  padding: var(--wrap-y) var(--wrap-x);
}
.wrap--hero   { --wrap-y: 82px; padding-bottom: 68px; }
.wrap--hero-l { --wrap-y: 86px; padding-bottom: 78px; }
.wrap--cta    { --wrap-y: 88px; text-align: center; }
.wrap--cta-l  { --wrap-y: 96px; text-align: center; }
.wrap--strip  { --wrap-y: 34px; }
.wrap--slim   { --wrap-y: 64px; }
.wrap--flush   { padding-top: 0; }
.wrap--flush-b { padding-bottom: 0; }
/* The editorial measure. A narrower column is a reading decision, not a new
   container -- so it is a modifier on .wrap, never a separate class. */
.wrap--reading { max-width: var(--wrap-read); }

/* -- Grid ------------------------------------------------------------------ */
.grid { display: grid; gap: var(--gap); }

.grid--2       { grid-template-columns: repeat(2, 1fr); }
.grid--3       { grid-template-columns: repeat(3, 1fr); }
.grid--4       { grid-template-columns: repeat(4, 1fr); }
.grid--split   { grid-template-columns: 1fr 1fr; }
.grid--aside   { grid-template-columns: .7fr 1.3fr;   }  /* narrow title + prose */
.grid--acc     { grid-template-columns: .85fr 1.15fr; }  /* title + accordion   */
.grid--media   { grid-template-columns: .8fr 1.2fr;   }  /* cover + copy        */
.grid--video   { grid-template-columns: 1fr 1.35fr;   }
.grid--join    { grid-template-columns: 1.1fr .9fr;   }
.grid--pillars { grid-template-columns: 1.05fr 1.95fr; }
.grid--form    { grid-template-columns: 1.35fr .65fr; }
.grid--endcap  { grid-template-columns: 1fr auto; }      /* copy + button       */
.grid--label   { grid-template-columns: auto 1fr; }      /* side label + block  */

.grid--gap-lg  { gap: var(--gap-lg); }
.grid--gap-sm  { gap: var(--gap-sm); }
.grid--gap-0   { gap: 0; }
.grid--middle  { align-items: center; }
.grid--top     { align-items: start; }
.grid--bottom  { align-items: end; }

/* -- Section head: eyebrow + title on the left, control on the right -------- */
.sec-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 16px;
}
.sec-head--baseline { align-items: baseline; }

/* -- Small helpers --------------------------------------------------------- */
.row       { display: flex; align-items: center; gap: 12px; }
.row--wrap { flex-wrap: wrap; }
.stack     { display: flex; flex-direction: column; }
.center    { text-align: center; }
.mt-0  { margin-top: 0; }
.mt-sm { margin-top: 18px; }
.mt-md { margin-top: 26px; }
.mt-lg { margin-top: 34px; }

/* Width constraints for pull-aside blocks (testimonial slots, callouts) */
.measure        { max-width: 600px; }
.measure--sm    { max-width: 560px; }
.measure--lg    { max-width: 640px; }


/* ============================================================================
   4  TYPOGRAPHY
   ============================================================================ */

/* -- Labels ----------------------------------------------------------------
   Two classes cover every piece of mono type on the site.

     .eyebrow  uppercase -- a kicker naming the section or the field
     .label    sentence case -- a value, a date, a count, a filename

   Both read the label scale, so a size change is one edit rather than 74. Any
   component needing mono type uses one of these; none sets font-family itself.
   ------------------------------------------------------------------------- */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--band-eyebrow);
}
/* An eyebrow that opens with a mark (hc_section_icon) lays itself out as a row
   so the icon sits on the label's optical centre rather than the text baseline.
   The rule is on .eyebrow itself rather than a modifier class because the icon
   is the only child that triggers it -- an eyebrow that is plain text is
   unaffected by a flex row containing one item.

   The glyph is set in em so it tracks whatever --label size the eyebrow is
   using, and gets a little optical lift: a 24px box on uppercase mono sits low
   against the caps if it is aligned centre. */
.eyebrow:has(.eyebrow__icon),
.feature__num:has(.eyebrow__icon) { display: flex; align-items: center; gap: .62em; }
/* :has() is not universal; without it the icon still sits inline beside the
   label, which is the same reading, just without the optical centring. */
@supports not selector(:has(*)) {
  .eyebrow__icon { vertical-align: -.28em; margin-right: .5em; }
}
.eyebrow__icon {
  width: 1.32em; height: 1.32em;
  flex: none;
  margin-top: -.08em;
  opacity: .9;
}
.eyebrow--hero   { font-size: var(--label-lg); letter-spacing: var(--track-lg); color: var(--gold-text); margin-bottom: 24px; display: block; }
.eyebrow--accent { color: var(--accent); }
.eyebrow--sm     { font-size: var(--label-sm); letter-spacing: var(--track-sm); }
.eyebrow--tight  { letter-spacing: var(--track-sm); }

/* Mono, but read as a value rather than a heading -- so no caps and much less
   tracking, which would only slow the eye down on something it has to parse. */
.label {
  font-family: var(--font-mono);
  font-size: var(--label);
  letter-spacing: .04em;
  color: var(--sub);
}
.label--sm     { font-size: var(--label-sm); }
.label--accent { color: var(--accent); }
.eyebrow--vert   { writing-mode: vertical-rl; transform: rotate(180deg); }

/* -- Headings --------------------------------------------------------------
   Each class names its ROLE and takes the step that role sits at. The modifiers
   move a heading up or down the scale; none of them invents a size. */
.h-hero {
  font-size: var(--step-6);
  line-height: 1.0;
  font-weight: 900;
  letter-spacing: var(--tight-6);
  margin: 0;
  max-width: 16ch;
}
/* The hero is already the top step. --xl is the same size set tighter and on a
   closer line, for the one page whose headline is short enough to carry it. */
.h-hero--xl { line-height: .98; letter-spacing: -.04em; }
.h-hero--lg { line-height: 1.02; }

.h-section {
  font-size: var(--step-4);
  line-height: 1.04;
  font-weight: 900;
  letter-spacing: var(--tight-5);
  margin: 14px 0 0;
}
.h-section--sm { font-size: var(--step-3); letter-spacing: var(--tight-4); }
.h-section--md { font-size: var(--step-4); }
.h-section--lg { font-size: var(--step-5); letter-spacing: var(--tight-6); }

.h-cta {
  font-size: var(--step-5);
  line-height: 1.02;
  font-weight: 900;
  letter-spacing: -.04em;
  margin: 0;
}
.h-cta--sm { font-size: var(--step-4); letter-spacing: var(--tight-5); }
.h-cta--lg { font-size: var(--step-6); }

.h-card   { font-size: var(--step-2); font-weight: 800; letter-spacing: var(--tight-2); margin: 14px 0 0; }
.h-card--sm { font-size: var(--step-1); margin: 0; }
.h-card--md { font-size: var(--step-1); margin: 0; }
.h-card--lg { font-size: var(--step-3); font-weight: 900; letter-spacing: var(--tight-3); margin: 12px 0 0; line-height: 1.08; }

/* Gold emphasis inside a heading */
.hl { color: var(--gold); }
.hl--teal { color: var(--tealBright); }

/* -- Body copy ------------------------------------------------------------- */
.lede {
  font-size: 17px; line-height: 1.6;
  color: var(--sub);
  margin: 22px 0 0;
  max-width: 34ch;
}
.lede--wide { max-width: 52ch; }
.lede--sm   { font-size: 16px; max-width: 56ch; }
.lede--onband { color: var(--fade-soft); margin: 20px 0 0; max-width: 40ch; }

/* Supporting paragraph on a coloured band — no rule above it */
.band-lede { font-size: 18px; line-height: 1.6; color: var(--fade); margin: 20px 0 0; max-width: 46ch; }
/* Hidden on Applications: the filter row directly below it already says how
   many applications there are and offers the filtering, so the lede was
   narrating the control that sits under it. */
body[data-page="application"] .band-lede { display: none; }

.hero-lede {
  font-size: 19px; line-height: 1.55;
  color: var(--fade);
  max-width: 56ch;
  margin: 32px 0 0;
  border-top: 1px solid var(--band-hair);
  padding-top: 24px;
}
.hero-lede--lg { font-size: 20px; max-width: 60ch; color: var(--fade-strong); }

.cta-lede {
  font-size: 18px; line-height: 1.55;
  color: var(--fade);
  margin: 18px auto 0;
  max-width: 44ch;
}

.prose {
  font-size: 19px; line-height: 1.65;
  color: var(--prose);
  max-width: 60ch;
}
.prose p { margin: 0; }
.prose p + p { margin-top: 22px; }

.copy   { font-size: 17px; line-height: 1.65; color: var(--prose); max-width: 52ch; margin: 22px 0 0; }
.copy--tight { margin-top: 18px; }
.copy--book  { font-size: 18px; max-width: 54ch; }

.note { font-size: 15px; line-height: 1.6; color: var(--sub); margin: 12px 0 0; }

/* Bulleted list for light bands — the .who__facts twin, which is drawn in
   --fade / --hair-light and so only works on the dark hero it lives in. */
.ticks {
  list-style: none;
  margin: 22px 0 0; padding: 0;
  display: grid; gap: 10px;
  max-width: 54ch;
}
.ticks li {
  position: relative;
  padding-left: 22px;
  font-size: 16px; line-height: 1.55;
  color: var(--prose);
}
.ticks li::before {
  content: "";
  position: absolute; left: 0; top: .5em;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
}

/* Deck line sitting under a short title (SATA / SAPM) */
.subtitle { font-size: 16px; font-weight: 600; color: var(--sub); margin-top: 4px; }

/* -- Mono meta ------------------------------------------------------------- */
.meta {
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  letter-spacing: .06em;
  line-height: 1.6;
  color: var(--sub);
}
.meta--upper { text-transform: uppercase; letter-spacing: .08em; }
.meta--lg    { font-size: 12px; }
.meta--accent{ color: var(--accent); }


/* ============================================================================
   5  CONTROLS
   ============================================================================ */
.btn {
  display: inline-block;
  padding: 16px 32px;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 15px;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  cursor: pointer;
  transition: opacity var(--t-fast);
}
.btn--gold   { background: var(--gold);   color: var(--ink); }
.btn--accent { background: var(--accent); color: #fff; }
/* Named for ink and filled with blue, which is why the nav button never quite
   matched the chrome around it. It is the chrome colour now, so the header CTA
   reads as part of the same frame as the strip above and the bar below. */
.btn--ink    { background: var(--chrome); color: var(--paper2); }
.btn--ghost  { background: transparent;   color: var(--band-fg); border-color: rgba(255,255,255,.6); }
/* Night-page primary: the glow is the hover state, so the button reads as a
   control rather than as one more block of colour on a dark surface. */
.btn--teal   { background: var(--tealBright); color: #05221C; transition: box-shadow var(--t-base), transform var(--t-base) var(--ease), opacity var(--t-fast); }
.btn--teal:hover { transform: translateY(-2px); box-shadow: 0 12px 30px rgba(47,191,163,.32); }

.btn--sm  { padding: 13px 24px; font-size: 14px; }
.btn--nav { padding: 10px 18px; font-size: 13.5px; }
.btn--md  { padding: 15px 28px; }

/* Understated mono link with a rule under it */
.link-arrow {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--label-lg);
  color: var(--ink);
  border-bottom: 1px solid currentColor;
  padding-bottom: 2px;
  text-decoration: none;
}
.link-arrow--onband { color: var(--band-fg); }
.link-arrow--gold   { color: var(--gold-text); }

/* Circular carousel control — shared by archive, activity and blog scrollers */
.nav-btn {
  width: var(--control-size); height: var(--control-size);
  border-radius: 50%;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  font-size: 18px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background var(--t-fast), color var(--t-fast);
}
.nav-btn:hover { background: var(--ink); color: var(--paper); }
.nav-btn.swiper-button-disabled { opacity: .3; cursor: default; }

/* -- Form fields ----------------------------------------------------------- */
.fld {
  width: 100%;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--ink);
  background: var(--paper2);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  padding: 14px 16px;
  outline: none;
}
.fld:focus { border-color: var(--blue); box-shadow: 0 0 0 3px rgba(18,55,66,.12); }
.fld::placeholder { color: var(--placeholder); }
textarea.fld { resize: vertical; }

.field { margin-top: 16px; }
.field__label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  letter-spacing: var(--track-sm);
  text-transform: uppercase;
  color: var(--sub);
  margin-bottom: 8px;
}

.honeypot { position: absolute; left: -9999px; }


/* ============================================================================
   6  COMPONENTS
   ============================================================================ */

/* -- Site header ----------------------------------------------------------- */
/* No rule under the header. The announcement strip above it and the coloured
   banner below already give the nav two hard edges of its own; a third line
   between them was one boundary too many. */
.site-headwrap {
  background: var(--paper);
}

.topbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 10px var(--wrap-x);
  background: var(--chrome);
  color: var(--paper2);
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  letter-spacing: .1em;
  /* Dark strip, same reasoning as the footer. */
  --accent: var(--gold);
  --focus: var(--gold);
}
.topbar__mid { text-align: center; opacity: .7; }
.topbar__end { text-align: right; }

.nav {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  padding: 20px var(--wrap-x);
  max-width: var(--wrap-max);
  margin: 0 auto;
}
.nav__brand {
  font-weight: 900;
  font-size: 21px;
  letter-spacing: -.02em;
  color: var(--ink);
  text-decoration: none;
}
.nav__links { display: flex; gap: 26px; align-items: center; }
.nav__link {
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  color: var(--ink);
  border-bottom: 2px solid transparent;
  padding-bottom: 3px;
}
.nav__link:hover { color: var(--blue); border-bottom-color: var(--muted); }
.nav__link[aria-current="page"] { color: var(--blue); border-bottom-color: var(--blue); }

.nav-toggle {
  display: none;
  background: none; border: 0; cursor: pointer;
  padding: 8px; margin-left: auto;
}
.nav-toggle span {
  display: block; width: 24px; height: 2px;
  background: var(--ink); margin: 5px 0;
  transition: transform var(--t-fast), opacity var(--t-fast);
}

/* -- Site footer ----------------------------------------------------------- */
/* The footer is a dark surface but not a .band, so it names its own accent --
   the root default is the navy that would vanish against this ground. Gold is
   what every other dark surface on the site uses. */
.site-footer {
  background: var(--chrome);
  color: var(--paper2);
  font-family: var(--font-sans);
  --accent: var(--gold);
  --focus: var(--gold);
}
.site-footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 48px;
  max-width: var(--wrap-max);
  margin: 0 auto;
  padding: 64px var(--wrap-x) 40px;
}
.site-footer__brand { font-weight: 900; font-size: 24px; letter-spacing: -.02em; }
.site-footer__blurb {
  font-size: 15px; line-height: 1.6;
  color: rgba(243,237,225,.62);
  margin: 14px 0 0; max-width: 34ch;
}
.site-footer__heading {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--gold-text);
  margin-bottom: 16px;
}
/* The CTA is its own grid cell rather than a trailing line of the brand column.
   On desktop the brand cell is told to span both rows and the CTA is placed in
   the first column's second row directly under it, so the corner reads exactly
   as it did. Stacked on a phone the grid keeps DOM order, which puts the CTA
   after the link lists: the last thing in the footer, which is where an action
   belongs once the reader has scrolled past everything else. */
.site-footer__grid > :first-child { grid-column: 1; grid-row: 1; }
.site-footer__cta { grid-column: 1; grid-row: 2; align-self: start; }
.site-footer__links { display: flex; flex-direction: column; gap: var(--gap-xs); font-size: 14.5px; }
.site-footer__links a { color: rgba(243,237,225,.82); text-decoration: none; }
.site-footer__links span { color: var(--fade-weak); }
.site-footer__legal { max-width: var(--wrap-max); margin: 0 auto; padding: 0 var(--wrap-x) 40px; }
.site-footer__legal-inner {
  border-top: 1px solid rgba(243,237,225,.16);
  padding-top: 22px;
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px; flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .06em;
  color: var(--fade-weak);
}

/* -- Hero ------------------------------------------------------------------ */
.hero__meta {
  display: flex; gap: 36px; flex-wrap: wrap;
  margin-top: 34px; padding-top: 24px;
  border-top: 1px solid var(--band-hair);
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: .06em;
  color: rgba(234,241,236,.72);
}
.hero__actions { display: flex; gap: 16px; margin-top: 34px; flex-wrap: wrap; }

/* Home hero: typewriter line + button share a rule */
.hero__foot {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 40px;
  align-items: end;
  margin-top: 40px;
  padding-top: 28px;
  border-top: 1px solid var(--band-hair);
}
.hero__foot p { font-size: 19px; line-height: 1.55; color: var(--fade); max-width: 56ch; margin: 0; }

/* Pill badge used on the Outreach hero */
.hero__badge {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: var(--track-lg); text-transform: uppercase;
  color: var(--on-green);
  background: var(--hair-lighter);
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  margin-bottom: 26px;
}
.hero__badge::before {
  content: ""; width: 8px; height: 8px;
  border-radius: 50%; background: var(--gold);
}
/* Applications hero: the dot breathes, the way a status light does */
.hero__badge--live {
  color: var(--tealBright);
  background: rgba(47,191,163,.10);
  border: 1px solid var(--hair-night);
}
.hero__badge--live::before { background: var(--tealBright); animation: pulseDot 2.4s var(--ease) infinite; }

/* -- Outreach hero: the green aura ------------------------------------------
   Everything decorative lives in .hero-out__fx behind the copy, so the hero is
   a normal band the moment any of it is switched off. Two soft radial washes
   drift on their own; a third follows the pointer and only exists once JS
   starts writing --mx / --my (fine pointers only — see enhance.js). No blur
   filters: the gradients are already soft, and a 600px blur on a full-bleed
   band is the one thing that would make this hero expensive to paint. */
.hero-out { position: relative; isolation: isolate; overflow: hidden; }
/* This headline is three deliberate lines (the breaks are in the markup), where
   the other three banners run two. At the --lg step that made it the one hero
   taller than the rest, so it drops to the base hero step -- still the largest
   type on the page, and now the same banner height as everywhere else. */
.hero-out__wrap { position: relative; z-index: 1; }
.hero-out .h-hero { font-size: var(--step-5); }
.hero-out .hero-lede { font-size: 17px; }

.hero-out__fx {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* Fine dot texture, faded out towards the left so it never competes with the
   headline sitting over it */
/* This page's own dot grid is gone -- the shared .hero-band__fx carries it for
   every banner now, so .hero-out__fx is only the pointer-tracked light. */

/* The fade that used to hand this hero off to the stat strip is gone: the
   pagenav sits directly below every banner now, and the other three end on a
   clean edge. One banner darkening at the bottom was the last thing making
   this page's hero read as a different object from the rest.
*/

/* A single gold wash, low and wide, so the gold in the badge, the highlighted
   words and the buttons reads as one warm light rather than three spot colours */

.hero-out__light {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity .55s var(--ease);
  background: radial-gradient(560px circle at var(--mx, 50%) var(--my, 38%), rgba(217,172,91,.15), transparent 62%);
}
.hero-out.is-lit .hero-out__light { opacity: 1; }

/* The badge dot breathes the way the Applications status light does, in gold */
.hero-out .hero__badge {
  background: rgba(255,255,255,.10);
  border: 1px solid var(--hair-light);
  backdrop-filter: blur(2px);
}
.hero-out .hero__badge::before { animation: pulseGold 2.8s var(--ease) infinite; }

/* The highlighted words get their rule drawn under them once the headline has
   finished rising, so the eye lands on the phrase the page is arguing for. */
.hero-out .hl { position: relative; }
.hero-out .hl::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -.07em;
  height: 3px; border-radius: var(--r-xs);
  background: currentColor; opacity: .5;
  transform: scaleX(0); transform-origin: 0 50%;
  animation: drawRule .85s var(--ease) 1s forwards;
}

/* Hero buttons lift rather than just fading, so the two routes out of the hero
   feel like controls. Gold keeps its own glow; the ghost fills instead. */
.hero-out .btn {
  transition: transform var(--t-base) var(--ease), box-shadow var(--t-base),
              background var(--t-fast), border-color var(--t-fast), opacity var(--t-fast);
}
.hero-out .btn:hover { transform: translateY(-2px); }
.hero-out .btn--gold:hover  { box-shadow: 0 14px 32px rgba(217,172,91,.34); }
.hero-out .btn--ghost:hover { background: rgba(255,255,255,.12); border-color: var(--paper); }

/* -- Masthead: the homepage split hero + the four doors ----------------------
   Copy left, cut-out portrait right, the name ghosted oversized behind both,
   and one dark bar of four doors along the bottom.

   The portrait is a studio shot on a near-white ground, so unlike a cropped
   photograph it is not a rectangle that has to be masked into place -- it is a
   figure standing on the page. That decides three things:

     * the band is LIGHT and lightly textured, so the white of the photograph
       melts into it and no seam has to be hidden;
     * the picture sits in normal flow at its own aspect, bottom-aligned, so the
       figure stands ON the door bar rather than floating over it;
     * the pattern is a CSS gradient, not an image -- it costs nothing, scales
       to any viewport, and can never fail to load.

   Nothing here depends on the photograph arriving: .masthead--nophoto collapses
   the split to one column. */
.masthead {
  position: relative;
  isolation: isolate;
  /* Both axes: the ghost name and the portrait are both deliberately wider than
     their columns, and clipping is what makes that safe rather than a
     horizontal scrollbar on the whole document. */
  overflow: hidden;
  --band-bg: #fff;
  --band-fg: var(--ink);
  --accent:  var(--gold-deep);
  --band-eyebrow: var(--sub);
  --band-hair: var(--line);
}

/* The pattern: a fine grid, masked to a soft corner wash. Pure gradients at very
   low contrast -- this is paper texture, not decoration, and it must never
   compete with the portrait or the type. */
.masthead::before {
  content: "";
  position: absolute; inset: 0;
  z-index: 0;
  background-image:
    linear-gradient(rgba(27,23,18,.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(27,23,18,.028) 1px, transparent 1px);
  background-size: 46px 46px, 46px 46px;
  -webkit-mask-image: radial-gradient(120% 90% at 30% 20%, #000 40%, transparent 100%);
          mask-image: radial-gradient(120% 90% at 30% 20%, #000 40%, transparent 100%);
  pointer-events: none;
}

/* The ghost name. Set at a size that only works because it is allowed to run
   off both edges -- it is texture, not a word to be read, which is why it is
   aria-hidden in the markup and why clipping it is correct, not a bug. */
.masthead__ghost {
  position: absolute;
  top: 46%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 0;
  font-size: clamp(120px, 18vw, 290px);
  font-weight: 900;
  letter-spacing: -.05em;
  line-height: .82;
  white-space: nowrap;
  color: var(--ink);
  opacity: .045;
  user-select: none;
  pointer-events: none;
  max-width: 100vw;
  overflow: hidden;
}

/* The split. Both columns are bottom-aligned so the figure stands on the bar. */
/* The band is sized to the VIEWPORT, not to its contents -- the doors are the
   point of this hero and they must never fall below the fold.

   min-height alone does not do this: it sets a floor, and content taller than
   the floor simply pushes past it. The band needs a CEILING as well, with the
   copy+portrait row allowed to absorb the difference. So:

     height  = the viewport minus the chrome above  (the ceiling)
     .masthead__inner  flex: 1 + min-height: 0      (the row that gives way)
     .masthead__doors  flex: none                   (the row that never does)

   dvh rather than vh so a phone's collapsing address bar cannot cut the doors
   off. --mast-chrome is the topbar + nav measured at 110px; it is a variable so
   the two breakpoints below can restate it rather than repeat the calc. */
.masthead {
  --mast-chrome: 110px;
  height: calc(100dvh - var(--mast-chrome));
  min-height: 560px;      /* below this the hero stops being usable; scroll instead */
  max-height: 900px;      /* on a very tall screen it should not become a void */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.masthead__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .92fr);
  align-items: end;
  gap: var(--gap-lg);
  /* .masthead is a flex column, so .wrap's `margin:0 auto` no longer centres
     this row -- as a flex item it would shrink to its content and take the
     measure away from the copy. Stating the width restores the 1280px column. */
  width: 100%;
  align-self: center;
  padding-top: 0;
  padding-bottom: 0;
  flex: 1;
  min-height: 0;   /* let the row shrink inside the flex column */
}

.masthead__copy { padding: 40px 0 36px; max-width: 46ch; align-self: center; min-width: 0; }

.masthead__eyebrow {
  display: block;
  margin-bottom: 24px;
  color: var(--sub);
  font-size: var(--label-lg);
  letter-spacing: var(--track-lg);
}

/* The reference sets the name in caps at the widest the column allows, which is
   a different voice from the other heroes on the site -- so it states its own
   case and tracking rather than borrowing .h-hero--xl. */
/* This headline is a SENTENCE, not a name, so it drops the caps the previous
   wordmark treatment used -- caps at this length stop being readable -- and
   takes a measure instead. */
.masthead__title {
  max-width: 16ch;
  letter-spacing: -.035em;
  line-height: 1.0;
  font-size: clamp(34px, 4.4vw, 62px);
}

/* The emphasis. The retired blue hero underlined "what to do about it" as the
   phrase the page argues for; the same phrase carries the colour here, so the
   headline still has a point of focus rather than being one flat block.

   --gold-deep rather than --gold: this band is PAPER, and the display gold
   measures 1.8:1 on it. The deep step clears AA. The rule underneath is drawn
   after the headline settles, which is the one flourish this hero makes. */
/* Not nowrap: forcing the phrase onto one line pushed "and" onto a line of its
   own and left an orphan. It wraps like any other text; the rule below is drawn
   per line box, which is the correct behaviour for a phrase that may break. */
.masthead__hl {
  font-style: normal;
  color: var(--gold-deep);
  position: relative;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
/* The colour alone carries the emphasis -- no rule under it. */
.masthead__hl {
  animation: hlIn .7s var(--ease) .35s both;
}
@keyframes hlIn {
  from { opacity: .35; }
  to   { opacity: 1; }
}


/* The rule above the lede is the short measured line the reference draws under
   the name -- not a full-width divider. */
.masthead__lede {
  font-size: 17.5px; line-height: 1.55;
  color: var(--prose);
  max-width: 46ch;
  margin: 24px 0 0;
  padding-top: 22px;
  position: relative;
}
.masthead__lede::before {
  content: "";
  position: absolute; top: 0; left: 0;
  width: 88px; height: 2px;
  background: var(--ink);
}

/* The signature. No script face is loaded for this site, so it is set in the
   display face italic at a size that reads as a sign-off rather than pretending
   to be handwriting the page does not have. */
.masthead__sign {
  display: inline-block;
  margin-top: 22px;
  font-size: 30px;
  font-weight: 500;
  font-style: italic;
  letter-spacing: -.02em;
  color: var(--ink);
  text-decoration: none;
  opacity: .9;
  transition: opacity var(--t-fast) var(--ease);
}
.masthead__sign:hover { opacity: 1; text-decoration: underline; text-underline-offset: 6px; }

/* The portrait stands at the bottom of its column: a figure, not a framed
   picture, so it is never cropped and never masked. */
/* The cell is STRETCHED to the row and contributes no intrinsic height of its
   own. That second part is the whole trick: a grid item sized by its content
   will happily exceed its track, which is what let the figure grow past the top
   of the band and get clipped. Here the cell is exactly the row, and the
   picture is laid out inside it. */
.masthead__photo {
  position: relative;
  align-self: stretch;
  justify-self: stretch;
  /* min-width:0 as well as min-height:0. A grid item's automatic minimum size is
     its CONTENT, so without this the picture's intrinsic width props the track
     open and steals the measure from the copy column beside it. */
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  pointer-events: none;
}
/* Absolutely positioned and pinned on all four sides, so the picture is
   measured against the CELL and contributes no height of its own -- that is
   what stops the figure growing past the top of the band on a short screen.
   object-fit keeps its proportions inside that box and object-position keeps it
   standing on the bottom edge, so a short viewport shrinks the figure rather
   than cropping his head.

   The supplied file had its editor's transparency checker flattened into the
   pixels, so assets/img/henry-hero-cutout.png is the same photograph with a
   real alpha channel recovered from it (flood fill inward from the border,
   which keeps the light shirt that a luma threshold would have eaten). Because
   the figure is genuinely cut out it needs no blend mode and no mask -- it just
   stands on the band. */
.masthead__photo img {
  position: absolute;
  top: 16px;                  /* headroom: never flush against the nav above */
  bottom: -1px;               /* sit ON the door bar, with no hairline gap */
  left: 0; right: 0;
  width: 100%; height: auto;
  margin: auto;
  max-height: calc(100% - 15px);
  object-fit: contain;
  object-position: bottom center;
  filter: drop-shadow(0 24px 38px rgba(27,23,18,.16));
}

/* No photograph: one column, and the copy takes the full measure. */
.masthead--nophoto .masthead__inner { grid-template-columns: minmax(0, 1fr); min-height: 0; }
.masthead--nophoto .masthead__copy  { padding: 92px 0 76px; }

/* -- The four doors ---------------------------------------------------------
   At rest this is ONE dark object spanning the band, divided by hairlines --
   which is what makes the four roles read as one set of choices rather than
   four unrelated cards. On interaction a single door detaches: it takes the
   colour of the page it opens, lifts, and casts a shadow, so the colour answers
   "where does this go" rather than being decoration.

   Everything that moves is transform, opacity or colour. Nothing reflows, so
   running the pointer along the bar cannot make the page jump. */
.masthead__doors {
  position: relative;
  z-index: 2;
  flex: none;              /* the one row that never gives way */
  width: 100%;
  max-width: var(--wrap-max);
  margin: 0 auto;
  padding: 0 var(--wrap-x);
}

.doors {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  /* No ground and no shared shadow: the doors are four separate cards, each
     carrying its own colour and its own elevation. The bar must not clip
     either -- a lifted door and its sheen have to escape the row.

     One shared perspective, so the four tilt toward a single vanishing point
     instead of each having its own. */
  perspective: 1400px;
  perspective-origin: 50% 50%;
}
/* Every door is rounded on all four corners. Squaring the middle two only made
   sense while the bar was one fused strip; now that each door carries its own
   colour they read as four cards, and two of them missing corners looked like a
   bug rather than a join. */

.door {
  --door-bg: var(--blueHi);
  --door-fg: var(--on-blue);
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 18px;
  padding: 30px 26px 32px;
  background: var(--door-bg);
  color: var(--door-fg);
  text-decoration: none;
  border-radius: var(--radius-surface);
  /* One transform chain driven entirely by custom properties, so the three
     things that move a door never fight over the same declaration: the pointer
     tilt (--rx/--ry), the hover lift (--ty) and the scale (--sc) each own a
     slot and compose here. Same contract as .appcard on the Applications page,
     which is where this interaction comes from. */
  transform: perspective(var(--persp, 800px))
             rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg))
             translate3d(0, var(--ty, 0px), var(--tz, 0px))
             scale(var(--sc, 1));
  /* The children are positioned in the SAME 3D space as the door, so when it
     tilts they sit at their own depths instead of being painted flat onto its
     face. That parallax between the icon, the copy and the surface is what
     actually reads as three dimensions -- a rotated flat rectangle does not. */
  transform-style: preserve-3d;
  transition: transform var(--t-slow) var(--ease),
              box-shadow var(--t-slow) var(--ease);
}
/* While the pointer is on a door the tilt must feel attached to the cursor,
   not eased behind it. */
.door.is-tilting { transition: transform .12s linear, box-shadow var(--t-slow) var(--ease); }

/* The pointer sheen: a soft highlight that follows the cursor.

   soft-light rather than a flat white wash. A wash composited over the ground
   lightened the exact area being read; soft-light lifts only where the ground
   is already light and leaves the shadowed areas alone, the way a real specular
   highlight behaves, so it cannot drag the mid-tones up under the text.

   z-index 1: above the rising ground, below the copy. */
.door__glow {
  position: absolute; inset: 0;
  z-index: 1;
  border-radius: inherit;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
                              rgba(255,255,255,.42), transparent 60%);
  mix-blend-mode: soft-light;
  opacity: 0;
  transition: opacity var(--t-base) var(--ease);
  pointer-events: none;
}
.door.is-lit .door__glow { opacity: 1; }

/* Everything the reader looks at sits above the wash and the glow -- and at its
   own DEPTH, so the tilt separates the layers instead of rotating one flat
   picture. The icon leads, the label follows, the blurb sits nearly on the
   surface: the further forward an element is, the more it parallaxes. */
.door__icon, .door__body { position: relative; z-index: 2; }
.door__icon  { transform: translateZ(var(--z-icon, 0px)); }
.door__role  { transform: translateZ(var(--z-role, 0px)); }
.door__blurb,
.door__cta   { transform: translateZ(var(--z-copy, 0px)); }
.door__icon, .door__role, .door__blurb, .door__cta {
  transition: transform var(--t-slow) var(--ease);
}
.door:hover, .door:focus-visible {
  --z-icon: 46px;
  --z-role: 28px;
  --z-copy: 16px;
}

/* The "power on" hairline: a lit edge that draws itself across the top of the
   door on hover. Gold against every one of the four grounds. */
.door__edge {
  position: absolute; top: 0; left: 0; right: 0;
  height: 2px;
  z-index: 4;
  background: linear-gradient(90deg, var(--gold), var(--gold-text));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-slow) var(--ease);
  border-radius: inherit;
}
.door:hover .door__edge,
.door:focus-visible .door__edge { transform: scaleX(1); }

/* No dividers: with a gap between them the cards separate themselves, and a
   hairline inside a rounded card would read as a stray mark. */

/* The colour wash, on its own layer so the text above never has to cross-fade.
   It scales up from the bottom edge, so the colour arrives as a movement. */
/* The hover ground rises from the bottom edge, so hovering reads as the door
   filling rather than as a flat colour swap.

   z-index 0, not -1: .door carries `isolation: isolate`, and a negative index
   inside an isolated stacking context paints BEHIND the element's own
   background -- so the previous -1 never appeared at all, and the sheen above
   it blended against a stale layer. That was the washed-out card.

   Every door fills to the SAME ground. Four hover colours meant four different
   contrast problems to solve; one shared scheme -- the footer's -- is a single
   pair to verify, and it reads as one component rather than four. */
.door::after {
  content: "";
  position: absolute; inset: 0;
  z-index: 0;
  background: var(--chrome);
  border-radius: inherit;
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform var(--t-base) var(--ease);
}

/* The lift. Two shadows: a neutral one for depth, and a second tinted with the
   door's own lit colour so the card appears to cast its light onto the band --
   which is what reads as "futuristic" rather than merely raised. A hair of
   saturation lifts the whole card with it. */
/* No colour filter here. saturate() moves luminance as well as saturation and
   would undo the contrast the lit tones above were chosen for; the lift, the
   depth, the shadow and the lit edge carry the interaction instead.

   --tz pushes the door toward the viewer through the perspective origin, which
   is what makes it grow from its own centre rather than simply scaling. The
   scale is kept small because the Z translation is already doing most of it. */
.door:hover,
.door:focus-visible {
  --ty: -12px;
  --tz: 38px;
  --sc: 1.005;
  box-shadow: 0 30px 60px -12px rgba(27,23,18,.42),
              0 12px 24px -8px rgba(27,23,18,.26);
  z-index: 2;
}
.door:hover::after,
.door:focus-visible::after { transform: scaleY(1); }

/* Each door takes the band colour of the page it opens. Olive is the light one,
   so the card flips to ink text to stay readable. Note this no longer matches
   .band--olive, which shares the same ground but was deliberately set to carry
   white type with the other teasers -- the two are intentionally different. */
/* Four doors, four HUES -- at REST. Developer and Consultant previously sat on
   --night-3 (#1E4453) and --blueHi (#1C5568): two dark desaturated teals a few
   points apart, which read as the same colour side by side. Consulting moves to
   bronze, the colour its own page already uses for advisory work, so the four
   are olive, teal, bronze and green: warm/cool alternating along the bar, each
   distinct at a glance and each still its destination page's colour.

   On HOVER they all fill to one ground -- the footer's --chrome with --paper2
   text and a gold accent. That is deliberate: a per-door hover colour meant
   four separate contrast problems (green fell to 4.35:1 once the sheen was
   composited over it), whereas one shared scheme is a single pair to verify and
   it already appears elsewhere on the page. Measured, paper2 on chrome is
   17.4:1 -- there is no state in which this text is hard to read. */
.door--olive   { --door-bg: var(--olive);    --door-fg: var(--ink);      }
/* --night, the darkest step of the Applications palette, rather than --night-3.
   The Applications page runs on this family; taking its deepest tone keeps the
   door true to its destination while putting real distance between it and the
   Consulting blue beside it -- the two were #1E4453 and #1C5568, a few points
   apart and indistinguishable side by side. */
.door--night   { --door-bg: var(--night);    --door-fg: var(--on-night); }
/* Consulting's own hero band colour. */
.door--blue-hi { --door-bg: var(--blueHi);   --door-fg: var(--on-blue);  }
.door--green   { --door-bg: var(--green);    --door-fg: var(--on-green); }
/* Kept for any other use; no door on the masthead takes it now. */
.door--bronze  { --door-bg: var(--bronze);   --door-fg: var(--on-bronze);}

/* The one hover scheme, stated once. Overriding --door-fg rather than `color`
   is what makes it reach the whole card: .door and its descendants all resolve
   their colour through that variable, so setting it here re-points the icon,
   the label, the blurb and the CTA together. Setting `color` alone left the
   olive door's ink text in place over the new dark ground. */
.door:hover, .door:focus-visible {
  --door-fg: var(--paper2);
  --accent: var(--gold);
  color: var(--paper2);
}

.door__icon {
  display: grid; place-items: center;
  width: 52px; height: 52px;
  border-radius: 50%;
  border: 1px solid currentColor;
  opacity: .92;
  color: inherit;
  transition: border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease),
              transform var(--t-base) var(--ease);
}
/* On the shared dark ground every door's icon well behaves the same way, so the
   light-door exception the old per-colour scheme needed is gone. */
.door:hover .door__icon,
.door:focus-visible .door__icon {
  background: rgba(255,255,255,.10);
  border-color: var(--gold);
  color: var(--gold);
  transform: translateZ(var(--z-icon, 0px)) scale(1.06);
}
.door__icon svg { width: 24px; height: 24px; }

.door__body { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

.door__role {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.door__blurb { font-size: 13.5px; line-height: 1.5; opacity: .92; }
/* On the shared dark ground the blurb can run at full strength. */
.door:hover .door__blurb,
.door:focus-visible .door__blurb { opacity: 1; }
.door:hover .door__cta,
.door:focus-visible .door__cta { color: var(--gold); }

.door__cta {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  letter-spacing: var(--track);
  text-transform: uppercase;
  display: inline-flex; align-items: center; gap: 10px;
}
.door__arrow { display: inline-block; transition: transform var(--t-base) var(--ease); }
.door:hover .door__arrow,
.door:focus-visible .door__arrow { transform: translateX(8px); }

/* Reduced motion keeps every state change -- the colour, the shadow, the focus
   ring -- and drops only the travel. An interaction that cannot be seen is a
   worse outcome than one that does not slide. */
@media (prefers-reduced-motion: reduce) {
  .door, .door__icon, .door__arrow { transition: color var(--t-fast) linear; }
  .door:hover, .door:focus-visible {
    --ty: 0px; --tz: 0px; --sc: 1; --rx: 0deg; --ry: 0deg;
    --z-icon: 0px; --z-role: 0px; --z-copy: 0px;
  }
  .door__glow, .door__edge { display: none; }
  .door:hover .door__icon, .door:focus-visible .door__icon { transform: none; }
  .door:hover .door__arrow, .door:focus-visible .door__arrow { transform: none; }
  .door::after { transform: scaleY(1); opacity: 0; transition: opacity var(--t-fast) linear; }
  .door:hover::after, .door:focus-visible::after { opacity: 1; }
}

/* -- CTA band -------------------------------------------------------------- */
.cta__actions {
  display: flex; gap: 18px;
  justify-content: center; align-items: center;
  flex-wrap: wrap;
  margin-top: 32px;
}

/* -- Teaser banner (home → section handoffs) ------------------------------- */
.teaser { display: grid; grid-template-columns: 1fr auto; gap: 40px; align-items: center; }
.teaser .eyebrow { opacity: .78; color: var(--band-fg); }
.teaser h2 { max-width: 24ch; }
/* The banners stack directly on top of each other, so their buttons sit in a
   vertical column down the right edge. A shared min-width keeps that column a
   straight line instead of a ragged edge that follows the label lengths —
   min-width rather than a fixed width so the longest label can still grow. */
.teaser .btn {
  background: var(--band-fg); color: var(--band-bg);
  min-width: 330px; text-align: center;
}

/* -- Hero band -------------------------------------------------------------
   Every page banner is the same object: same height, same pattern, same bar
   underneath. The pages differ in colour and in what sits inside the banner --
   not in how tall it is or whether it has a texture.

   Before this there were four heroes at four heights (715 / 795 / 820 / 880px)
   with three different pattern treatments, and only one of them had a nav bar
   below it. Consulting was the one that read right, so its measurements are
   the ones here.

   .hero-band          the section. Sets the height and hosts the pattern.
   .hero-band__fx      the pattern layer, one element, aria-hidden.
   .pagenav            the wayfinding bar, directly below every hero.
   --------------------------------------------------------------------------- */
.hero-band {
  position: relative;
  overflow: hidden;
}
/* Matching padding is not the same as matching height -- with four different
   headline lengths and a portrait card on one page, equal padding still gave
   four different banners. So the height itself is fixed and the content is
   centred inside it. Consulting's banner is the measurement.

   min-height rather than height: a long headline on a narrow window must still
   be able to push the banner taller rather than overflow it. */
.hero-band { min-height: var(--hero-h, 576px); display: flex; align-items: center; }
.hero-band > .wrap,
.hero-band > .wr-wrap { width: 100%; position: relative; z-index: 1; }
/* The wrap keeps a floor of padding so the banner never crowds its own edges,
   but the height above is what actually sets the size. */
.hero-band > .wrap { --wrap-y: 48px; padding-bottom: 48px; }
.hero-band > .wr-wrap { padding-top: 48px; padding-bottom: 48px; }

/* The pattern layer. Same size, same placement, same weight on every banner --
   and a DIFFERENT figure per page, because the texture is a chance to say what
   the page is about rather than another thing to make uniform.

   What is shared: the element, the mask that fades it out before it reaches the
   headline, the opacity, and the colour, which follows the band's own
   foreground so a light banner gets dark marks without a second rule.
   What differs: the figure itself, set per page below. */
.hero-band__fx {
  position: absolute; inset: 0;
  pointer-events: none;
  /* Falls back to the page ink for heroes that are not .band elements -- the
     Writing hero is a .wr-band, which never sets --band-fg. */
  color: var(--band-fg, var(--w-ink, var(--ink)));
  opacity: .10;
  -webkit-mask-image: radial-gradient(115% 85% at 82% 12%, #000 8%, transparent 68%);
          mask-image: radial-gradient(115% 85% at 82% 12%, #000 8%, transparent 68%);
}

/* APPLICATIONS -- a technical grid. Software is drawn on graph paper before it
   is built, and the page is full of real UI screenshots sitting on a plane. */
body[data-page="application"] .hero-band__fx {
  background-image:
    repeating-linear-gradient(to right,  currentColor 0 1px, transparent 1px 64px),
    repeating-linear-gradient(to bottom, currentColor 0 1px, transparent 1px 64px);
}

/* OUTREACH -- a scatter of dots, one per person. The Foundation's work is
   individual people in rooms, so the figure is countable rather than ruled. */
body[data-page="outreach"] .hero-band__fx {
  background-image: radial-gradient(currentColor 1.5px, transparent 2px);
  background-size: 34px 34px;
  opacity: .13;
}

/* Consulting and Writing carry no pattern: both banners already hold a strong
   object -- the practitioner card on one, the book jacket on the other -- and a
   texture behind them was competing rather than supporting. The two pages whose
   banners are type and colour alone are the ones that take a figure. */

/* -- Homepage praise -------------------------------------------------------
   The same component as the Writing page's Praise section: one quote at a time
   in a single grid cell, cross-fading in place. Sized for the site's own paper
   rather than the olive one, but the proportions are the Writing section's --
   small italic serif at a wide measure, so the longest quote needs few lines.
   -------------------------------------------------------------------------- */
body[data-page="home"] .sec-head--center,
.sec-head--center { display: block; text-align: center; }
.hq-quotes { display: grid; margin-top: 26px; align-items: center; }
.hq-quote {
  grid-area: 1 / 1;
  align-self: center;
  margin: 0;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-slow) var(--ease);
}
.hq-quote.is-active { opacity: 1; visibility: visible; }
.hq-quote__text {
  /* The site's sans, not a serif. The homepage does not load Cormorant, so a
     serif here would fall back to Georgia -- a fourth face on the page for one
     block. The proportions carry the Writing section's feel without it. */
  font-family: var(--font-sans);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(17px, 1.4vw, 20px);
  line-height: 1.7;
  color: var(--prose);
  margin: 0 auto;
  max-width: 66ch;
}
.hq-quote__by {
  display: block;
  margin-top: 18px;
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  letter-spacing: var(--track-sm);
  text-transform: uppercase;
  color: var(--sub);
}
.hq-dots { display: flex; justify-content: center; gap: 2px; margin-top: 22px; }
.hq-dot {
  width: 44px; height: 44px;   /* the target is 44px; the mark inside is 9px */
  padding: 0; border: 0; background: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.hq-dot::before {
  content: '';
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--muted);
  transition: background var(--t-fast) var(--ease), width var(--t-fast) var(--ease);
}
.hq-dot[aria-pressed="true"]::before { width: 22px; border-radius: var(--r-pill); background: var(--accent); }

/* -- Credibility / stat strip ---------------------------------------------- */
.strip { display: grid; grid-template-columns: repeat(4, 1fr); }
.strip__item { padding: 0 30px; border-right: 1px solid var(--band-hair); }
.strip__item:first-child { padding-left: 0; }
.strip__item:last-child  { padding-right: 0; border-right: 0; }
.strip__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--sub); margin-bottom: 8px;
}
.strip__value { font-size: 18px; font-weight: 800; letter-spacing: -.01em; }
.strip__value--italic { font-style: italic; }

/* Dark stat variant (Outreach) */
.strip--dark { --band-hair: rgba(255,255,255,.18); }
.strip--dark .strip__value { font-size: 40px; font-weight: 900; letter-spacing: -.02em; color: var(--gold); }
.strip--dark .strip__label { color: rgba(234,241,236,.7); margin: 4px 0 0; }

/* -- Card ------------------------------------------------------------------ */
/* -- Card ------------------------------------------------------------------
   The most repeated object on the site, so it is one object. Its surface is
   four variables, which a page or a band sets once:

     --card-bg      the ground it sits on
     --card-line    its border
     --card-radius  per-instance override; defaults to the page's surface role
     --card-hover   the border colour on hover

   That is what lets Writing and About look nothing like the shared pages while
   still being the same component -- the parts that differ are the ones the eye
   reads as identity, not the ones it reads as structure.
   ------------------------------------------------------------------------- */
.card {
  background: var(--card-bg, var(--paper));
  border: 1px solid var(--card-line, var(--line));
  border-radius: var(--card-radius, var(--radius-surface));
  padding: var(--card-pad, var(--pad-card));
  transition: border-color var(--t-fast) var(--ease);
}
a.card:hover { border-color: var(--card-hover, var(--accent)); }

.card--on-paper   { --card-bg: var(--paper);  }
.card--on-paper2  { --card-bg: var(--paper2); }
.card--top        { border-top:  var(--stripe) solid var(--accent); }
.card--left       { border-left: var(--stripe) solid var(--accent); }
.card--pad-md     { --card-pad: 32px; }
.card--pad-lg     { --card-pad: 34px; }
/* A card that is a link to a longer thing -- a book, an essay, a case. */
.card--link       { display: block; text-decoration: none; color: inherit; }

.card__index { font-family: var(--font-mono); font-size: var(--label); color: var(--accent); }
.card__statement {
  font-size: 19px; line-height: 1.5; font-weight: 600;
  letter-spacing: -.01em; margin: 16px 0 0;
}

/* -- Media box ------------------------------------------------------------- */
.media {
  aspect-ratio: var(--media-ratio, 4/3);
  border-radius: var(--radius-surface);
  border: 1px solid var(--line);
  background:
    repeating-linear-gradient(45deg, var(--line) 0 9px, transparent 9px 18px),
    var(--media-surface, var(--paper2));
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.media--3-4  { --media-ratio: 3/4; }
.media--16-10{ --media-ratio: 16/10; }
.media--on-paper { --media-surface: var(--paper); }
.media--cap  { max-width: 340px; }

/* -- Selected articles: filterable text grid --------------------------------
   Text only, no image box: none of these posts carries a picture, and six
   placeholder panels would be filler standing in for nothing. The cells sit
   over a --line ground with 1px gutters so they read as one ruled table.
   Filtering and paging are in enhance.js, on the same data-wr-* hooks the
   homepage rotator uses.
   --------------------------------------------------------------------------- */
.artfilters {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  gap: 14px 22px;
  margin-top: 26px; padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.artfilters__field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.artselect {
  font-family: var(--font-sans); font-size: 14px; color: var(--ink);
  background: var(--paper2);
  border: 1px solid var(--line); border-radius: var(--radius-sm, 4px);
  padding: 12px 34px 12px 13px;      /* room for the arrow */
  min-height: 44px;                  /* a real touch target */
  min-width: 190px;
  cursor: pointer;
  /* The platform arrow is suppressed so both selects look identical across
     browsers; the chevron below replaces it. */
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%237A5230' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 12px 8px;
}
.artselect:hover { border-color: var(--bronze); }
.artselect:focus-visible { outline: 2px solid var(--bronze); outline-offset: 2px; }

/* This section sits on --paper2, so a card in --paper2 would vanish into it.
   The cards take the lighter --paper and a bronze-tinted hairline, which is the
   accent this page already runs, rather than the generic grey-beige --line. */
.artgrid .blog-card {
  background: var(--paper);
  border-color: rgba(122, 82, 48, .18);
}
.artgrid .blog-card:hover { border-color: rgba(122, 82, 48, .42); }
/* The hatched placeholder inherits the same bronze tint, so a card without a
   picture reads as part of the set rather than as a grey hole in it. */
.artgrid .blog-card-media {
  background:
    repeating-linear-gradient(45deg, rgba(122, 82, 48, .13) 0 9px, transparent 9px 18px),
    var(--paper2);
}
.artgrid .blog-card-media.has-img { background: var(--paper2); }

.artgrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-md, 22px);
}
/* One cell wraps one .blog-card so the wrapper can carry the filter state
   without the card needing to know it is being filtered. */
.artcell { display: flex; }
.artcell > .blog-card { width: 100%; }
.artcell.is-out,                     /* filtered away  */
.artcell.is-over { display: none; }  /* beyond the page */

/* Images are opt-in. With the toggle off the media block is not rendered at
   all — hidden rather than replaced, so no placeholder stands in for a picture
   that was never meant to appear. */
.artgrid--noimg .blog-card-media { display: none; }

.row--center { justify-content: center; }

@media (max-width: 900px) {
  .artgrid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .artgrid { grid-template-columns: 1fr; }
  .artfilters__field { flex: 1 1 100%; }
  .artselect { width: 100%; min-width: 0; }
}

/* -- Book frame ------------------------------------------------------------
   A jacket is not a photograph: it has its own edges, and cropping it to a
   ratio cuts the type off the cover. So the frame is a padded panel and the
   jacket sits inside it whole, centred, lifted by a shadow — the panel takes
   the ratio, the book keeps its own.

   Only ever the front cover goes in here. A wrap-around spread shrinks the
   front to a third of the width and puts the back cover where the eye lands. */
.bookframe {
  display: flex; align-items: center; justify-content: center;
  padding: 48px;                     /* identical on both frames */
  background: var(--paper2);
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  aspect-ratio: 4 / 3;               /* both panels the same shape and size */
}
.bookframe--sunk { background: var(--paper); }
/* The two jackets are not quite the same proportion (0.63 vs 0.67), so sizing
   by max-height alone leaves one visibly taller. Fixing the HEIGHT and letting
   width follow makes them read as the same object photographed twice. */
.bookframe__img {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  box-shadow: 0 18px 44px rgba(42, 30, 18, .22);
}
.bookframe__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .08em;
  color: var(--sub);
  text-align: center;
}

.media__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .08em;
  color: var(--sub);
  text-align: center;
  padding: 8px;
}

/* When a real image is present, drop the stripes and show the artifact whole */
.media.has-img {
  background-image: none;
  background-color: var(--media-surface, var(--paper2));
  padding: 0;
}
.media.has-img .hc-img { object-fit: contain; padding: 10px; }
.media.has-img.fit-cover .hc-img { object-fit: cover; padding: 0; }
.media.has-img.on-dark { --media-surface: var(--blue); }

.hc-img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* -- Definition rows (Problem / Approach / Status) -------------------------- */
.deflist { display: flex; flex-direction: column; gap: 16px; margin-top: 24px; }
.deflist__term {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--accent); margin-bottom: 5px;
}
.deflist__desc { font-size: 16px; line-height: 1.6; margin: 0; color: var(--prose); }

/* -- Row list (clients, bylines, mastheads) -------------------------------- */
.rowlist { display: flex; flex-direction: column; }
.rowlist__row {
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px;
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
}
.rowlist__row:last-child { border-bottom: 0; }
.rowlist__name { font-size: 18px; font-weight: 700; }
.rowlist--lg .rowlist__row {
  padding: 20px 0;
  align-items: baseline;
  border-bottom: 0;
  border-top: 1px solid var(--line);
}
.rowlist--lg .rowlist__row:last-child { border-bottom: 1px solid var(--line); }
.rowlist--lg .rowlist__name { font-size: 22px; font-weight: 800; letter-spacing: -.01em; }

/* -- Row hover ------------------------------------------------------------
   A name steps toward the reader and its label answers in the section accent,
   so the row reads as one object rather than two columns of text. The rule
   above the row is drawn by a pseudo-element that scales from the left, which
   animates without the row's own border-top jumping between two widths.

   Rows are not links, so there is no focus state to match: this is a reading
   affordance, not a target. Everything is confined to the section's own
   accent, which each column sets with .accent--*. */
.rowlist--lg .rowlist__row { position: relative; }
.rowlist--lg .rowlist__row::before {
  content: "";
  position: absolute; inset: -1px auto auto 0;
  width: 100%; height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-base) var(--ease);
}
.rowlist--lg .rowlist__row:hover::before { transform: scaleX(1); }

.rowlist--lg .rowlist__name,
.rowlist--lg .rowlist__row .meta {
  transition: transform var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.rowlist--lg .rowlist__row:hover .rowlist__name { transform: translateX(8px); }
.rowlist--lg .rowlist__row:hover .meta { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  /* The colour and the rule stay; only the travel goes. */
  .rowlist--lg .rowlist__name { transition: none; }
  .rowlist--lg .rowlist__row:hover .rowlist__name { transform: none; }
  .rowlist--lg .rowlist__row::before { transition: opacity var(--t-fast) linear; transform: scaleX(1); opacity: 0; }
  .rowlist--lg .rowlist__row:hover::before { opacity: 1; }
}

/* -- Numbered feature rows (signature talks) ------------------------------- */
.numrow {
  display: grid;
  grid-template-columns: 80px 1fr auto;
  gap: 28px;
  align-items: start;
  padding: 26px 0;
  border-top: 1px solid var(--line);
}
.numrow:last-child { border-bottom: 1px solid var(--line); }
.numrow__num { font-size: 28px; font-weight: 900; color: var(--accent); letter-spacing: -.02em; }
.numrow__title { font-size: 23px; font-weight: 800; letter-spacing: -.01em; margin: 0; }
.numrow__body { font-size: 15px; line-height: 1.6; color: var(--sub); margin: 8px 0 0; max-width: 64ch; }

/* -- Steps (how to engage) ------------------------------------------------- */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; }
.steps__item { padding: 0 30px; border-right: 1px solid var(--line); }
.steps__item:first-child { padding-left: 0; }
.steps__item:last-child  { padding-right: 0; border-right: 0; }
.steps__num { font-size: 44px; font-weight: 900; color: var(--accent); letter-spacing: -.03em; }
.steps__title { font-size: 19px; font-weight: 800; margin: 14px 0 0; }
.steps__body { font-size: 15px; line-height: 1.6; color: var(--sub); margin: 10px 0 0; }

/* -- Big figure pair (50+ articles / 1 book) -------------------------------- */
.figures { display: flex; gap: 40px; margin-top: 28px; }
.figures__num { font-size: 34px; font-weight: 900; color: var(--accent); letter-spacing: -.02em; }
.figures__label { font-family: var(--font-mono); font-size: var(--label-sm); color: var(--sub); margin-top: 4px; }

/* -- Chips / pills --------------------------------------------------------- */
.chips { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 24px; }
.chip {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--accent);
  background: rgba(27,23,18,.06);                              /* fallback */
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  padding: 8px 14px;
  border-radius: var(--radius-pill);
}
.chip--outline {
  color: rgba(243,237,225,.8);
  background: transparent;
  border: 1px solid rgba(255,255,255,.24);
  text-transform: none;
}

.badge {
  position: absolute; top: 14px; left: 14px; z-index: 2;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--paper);
  background: rgba(27,23,18,.55);
  padding: 5px 11px;
  border-radius: var(--radius-pill);
}
.badge--gold { color: var(--gold-text); }

/* -- Quote blocks ---------------------------------------------------------- */
.quote { font-size: 17px; line-height: 1.5; font-weight: 600; letter-spacing: -.01em; margin: 22px 0 8px; max-width: 44ch; }
.quote--lead { font-size: 21px; line-height: 1.45; font-weight: 700; margin: 20px 0; }
.quote__by { font-family: var(--font-mono); font-size: var(--label-sm); color: var(--sub); }
.quote__by--ruled { border-bottom: 1px solid var(--line); padding-bottom: 20px; }
.quote__role { opacity: .65; }

/* Testimonial figure (home grid) */
.testi {
  margin: 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-top: var(--stripe) solid var(--accent);
  border-radius: var(--radius-surface);
  padding: var(--pad-card);
}
.testi__quote { font-size: 16px; line-height: 1.55; font-weight: 600; letter-spacing: -.01em; margin: 0; }
.testi__by {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .06em;
  color: var(--sub);
  margin-top: 22px; line-height: 1.6;
}
.testi__role { opacity: .65; }

/* Testimonial slider (home) */
.testi-swiper { padding-bottom: 8px; }
.testi-swiper .swiper-slide { height: auto; display: flex; }
.testi-swiper .testi {
  flex: 1; display: flex; flex-direction: column;
  transition: transform var(--t-slow) var(--ease), box-shadow var(--t-slow);
}
.testi-swiper .testi:hover { transform: translateY(-6px); box-shadow: var(--shadow-lift); }
.testi-swiper .testi__by { margin-top: auto; padding-top: 22px; }

/* Gold treatment: the top rule alone is a 4px sliver, so the warm cast has to
   carry into the card body and the attribution rule to actually register. The
   quote itself stays ink — gold body copy on paper fails contrast. */
.testi.accent--gold {
  background: linear-gradient(180deg, rgba(217,172,91,.14), rgba(217,172,91,.03) 55%, transparent);
  background-color: var(--paper);
  /* Per-side, never the `border-color` shorthand — that would repaint the gold
     top rule that .testi draws from --accent. */
  border-left-color: rgba(217,172,91,.42);
  border-right-color: rgba(217,172,91,.42);
  border-bottom-color: rgba(217,172,91,.42);
  border-top-width: 5px;
}
.testi.accent--gold:hover { box-shadow: 0 18px 40px rgba(217,172,91,.26); }
.testi.accent--gold .testi__by { border-top: 1px solid rgba(217,172,91,.38); color: var(--bronze); }
.testi.accent--gold .testi__role { color: var(--sub); opacity: .8; }
/* Dots match the archive gallery: a static, full-width row under the cards with
   a pill-shaped active bullet. Do not turn on Swiper's dynamicBullets here — it
   centres its row with left:50% on a fixed inline width, which needs absolute
   positioning and so drifts off-centre against the static row below. */
.testi-swiper .swiper-pagination { position: static; margin-top: 22px; }
.testi-swiper .swiper-pagination-bullet { background: var(--muted); opacity: 1; width: var(--dot); height: var(--dot); transition: var(--t-base); }
.testi-swiper .swiper-pagination-bullet-active { background: var(--gold); width: var(--dot-active); border-radius: var(--r-pill); }

/* Compact expandable side-card */
.testi-box {
  margin: 0;
  background: var(--paper2);
  border: 1px solid var(--line);
  border-left: var(--stripe) solid var(--accent);
  border-radius: var(--radius-surface);
  padding: var(--pad-card-sm);
  transition: box-shadow var(--t-base), border-color var(--t-base);
}
.testi-box:hover { box-shadow: var(--shadow-soft); }
.testi-box__context {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 12px;
}
.testi-quote {
  margin: 0;
  font-size: 15.5px; line-height: 1.55; font-weight: 500;
  color: var(--ink);
  transition: max-height var(--t-slow) var(--ease);
}
.testi-quote.is-clamped {
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden;
}
.testi-quote.is-open { -webkit-line-clamp: unset; display: block; }
.testi-more {
  margin-top: 10px;
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: .06em;
  color: var(--accent);
  border-bottom: 1px solid currentColor;
}
.testi-more:hover { opacity: .75; }
.testi-box__by {
  margin-top: 16px; padding-top: 14px;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .05em;
  color: var(--sub);
}
.testi-box__who { color: var(--ink); font-weight: 600; }
.testi-box__role { opacity: .8; }

/* -- Work tile (image over label over title) -------------------------------- */
.tile { text-decoration: none; color: var(--ink); display: block; }
.tile__kicker {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .08em;
  color: var(--purple);
  margin: 16px 0 6px;
}
.tile__title { font-size: 20px; font-weight: 800; margin: 0; letter-spacing: -.01em; }


/* ============================================================================
   7  PAGE MODULES
   ============================================================================ */

/* -- Accordion (five transitions) ------------------------------------------ */
.acc-row {
  display: grid;
  grid-template-columns: 72px 1fr auto;
  gap: 20px;
  align-items: start;
  padding: 22px;
  cursor: pointer;
  border-top: 1px solid var(--line);
  background: transparent;
  transition: background var(--t-base);
}
.acc-row.is-open { background: var(--acc-open-bg, var(--paper2)); }
.acc--on-paper2 .acc-row.is-open { --acc-open-bg: var(--paper); }

/* Hover and focus speak the same language as the row lists: an accent rule
   wipes in over the row, the title steps toward the reader, the date warms to
   the accent. Drawn as a pseudo-element scaling from the left so the row's own
   1px border-top never has to change width and shove the rows below it.

   The numeral and the +/- sign are deliberately left out: those two belong to
   the OPEN state, and reusing them here would make a hovered row look like an
   opened one. These rows are real controls, so focus-visible gets everything
   hover gets. */
.acc-row { position: relative; }
.acc-row::before {
  content: "";
  position: absolute; inset: -1px auto auto 0;
  width: 100%; height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-base) var(--ease);
}
.acc-row:hover::before,
.acc-row:focus-visible::before { transform: scaleX(1); }

.acc-title, .acc-meta { transition: transform var(--t-base) var(--ease), color var(--t-base) var(--ease); }
.acc-row:hover .acc-title,
.acc-row:focus-visible .acc-title { transform: translateX(8px); }
.acc-row:hover .acc-meta,
.acc-row:focus-visible .acc-meta { transform: translateX(8px); color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  /* Colour and rule stay; only the travel goes. */
  .acc-title, .acc-meta { transition: color var(--t-fast) linear; }
  .acc-row:hover .acc-title, .acc-row:focus-visible .acc-title,
  .acc-row:hover .acc-meta,  .acc-row:focus-visible .acc-meta { transform: none; }
  .acc-row::before { transition: opacity var(--t-fast) linear; transform: scaleX(1); opacity: 0; }
  .acc-row:hover::before, .acc-row:focus-visible::before { opacity: 1; }
}

.acc-num {
  font-weight: 900; font-size: 30px;
  color: var(--muted);
  line-height: 1; letter-spacing: -.02em;
  transition: color var(--t-base);
}
.acc-row.is-open .acc-num { color: var(--accent); }

.acc-title { font-size: 20px; font-weight: 800; letter-spacing: -.01em; }
.acc-meta {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--sub); margin-top: 4px;
}
.acc-body {
  max-height: 0; opacity: 0; margin: 0; overflow: hidden;
  transition: max-height .4s ease, opacity .4s ease, margin .4s ease;
}
.acc-row.is-open .acc-body { max-height: 320px; opacity: 1; margin: 12px 0 0; }
.acc-body p { font-size: 15px; line-height: 1.6; color: var(--sub); margin: 0; max-width: 52ch; }

.acc-sign {
  font-size: 24px; font-weight: 400; line-height: 1;
  color: var(--sub);
  transition: transform var(--t-base), color var(--t-base);
}
.acc-row.is-open .acc-sign { transform: rotate(45deg); color: var(--accent); }

/* -- Offerings slider ------------------------------------------------------ */
.slider-panel {
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: var(--stripe) solid var(--accent);
  border-radius: var(--radius-surface);
  padding: var(--pad-card-lg);
  display: grid;
  grid-template-columns: 190px 1fr;
  gap: 40px;
  align-items: center;
  min-height: 210px;
  transition: border-color var(--t-base);
}
.slider-panel__aside { border-right: 1px solid var(--line); padding-right: 32px; }
.slider-panel__num { font-size: 84px; font-weight: 900; line-height: 1; letter-spacing: -.03em; color: var(--accent); }
.slider-panel__tag {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  margin-top: 10px; color: var(--sub);
}
.slider-panel__title { font-size: 38px; font-weight: 900; letter-spacing: -.02em; margin: 0; line-height: 1.05; }
.slider-panel__desc { font-size: 18px; line-height: 1.6; color: var(--sub); margin: 16px 0 0; max-width: 52ch; }

.slider-dots { display: flex; gap: 10px; margin-top: 26px; align-items: center; }
.slide-dot {
  width: 10px; height: 10px;
  border-radius: var(--r-sm); border: none; padding: 0;
  cursor: pointer;
  background: var(--muted);
  transition: width var(--t-base) ease, background var(--t-base) ease;
}
.slide-dot.is-active { width: 28px; background: var(--accent); }
.slider-counter {
  margin-left: 12px;
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: .06em; color: var(--sub);
}

/* -- Archive gallery ------------------------------------------------------- */
.archive-swiper { padding-bottom: 8px; }
.archive-swiper .swiper-slide { height: auto; }

.archive-card {
  position: relative; display: block;
  aspect-ratio: 3/4;
  border-radius: var(--radius-surface);
  overflow: hidden;
  border: 1px solid var(--line);
  /* The image itself is set inline via background-image, NOT through a custom
     property: a relative url() inside a custom property resolves against this
     stylesheet (/assets/css/) instead of the document, which 404s. */
  background-color: var(--paper2);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  cursor: zoom-in;
  text-decoration: none;
  transition: transform var(--t-slow) var(--ease), box-shadow var(--t-slow);
}
.archive-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-lift); }
.archive-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.archive-card .cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 16px;
  background: linear-gradient(0deg, rgba(27,23,18,.82), rgba(27,23,18,0));
  display: flex; flex-direction: column; gap: 5px;
}
.archive-card .era { font-family: var(--font-mono); font-size: var(--label-sm); font-weight: 600; letter-spacing: .1em; color: var(--gold-text); }
.archive-card .lab { font-family: var(--font-mono); font-size: var(--label-sm); letter-spacing: .04em; color: var(--paper); }
.archive-card .zoom {
  position: absolute; top: 12px; right: 12px;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(27,23,18,.62); color: var(--paper);
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; opacity: 0; transform: scale(.8);
  transition: var(--t-fast);
}
.archive-card:hover .zoom { opacity: 1; transform: scale(1); }
.archive-card__hit { position: absolute; inset: 0; z-index: 3; }

.archive-swiper .swiper-pagination { position: static; margin-top: 22px; }
.archive-swiper .swiper-pagination-bullet { background: var(--muted); opacity: 1; width: var(--dot); height: var(--dot); transition: var(--t-base); }
.archive-swiper .swiper-pagination-bullet-active { background: var(--blue); width: var(--dot-active); border-radius: var(--r-pill); }

/* Timeline scale in the archive header */
.timescale { display: flex; align-items: center; gap: 12px; font-family: var(--font-mono); font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase; color: var(--sub); }
.timescale__now { color: var(--blue); }
.timescale__bar { width: 90px; height: 2px; background: linear-gradient(90deg, var(--blue), var(--line)); }

/* Lightbox theming */
.glightbox-clean .gslide-description { background: var(--chrome); }
.glightbox-clean .gslide-title { color: var(--paper); font-family: var(--font-sans); }
.glightbox-clean .gslide-desc  { color: var(--gold-text); font-family: var(--font-mono); font-size: var(--label); }

/* -- Blog cards ------------------------------------------------------------ */
.blog-scroll {
  display: flex; gap: 22px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 14px;
  scrollbar-width: thin;
  scrollbar-color: var(--muted) transparent;
}
.blog-scroll::-webkit-scrollbar { height: 8px; }
.blog-scroll::-webkit-scrollbar-track { background: transparent; }
.blog-scroll::-webkit-scrollbar-thumb { background: var(--muted); border-radius: var(--r-sm); }
.blog-scroll::-webkit-scrollbar-thumb:hover { background: var(--blue); }

.blog-card {
  display: flex; flex-direction: column;
  text-decoration: none; color: var(--ink);
  background: var(--paper2);
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  overflow: hidden;
  transition: transform var(--t-base) var(--ease), box-shadow var(--t-base), border-color var(--t-base);
}
.blog-card:hover { border-color: var(--blue); }
.blog-card:hover .blog-card-cta { color: var(--ink); }
.blog-card--scroll { flex: 0 0 300px; scroll-snap-align: start; }

.blog-card-media {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 16/10;
  background:
    repeating-linear-gradient(45deg, var(--line) 0 9px, transparent 9px 18px),
    var(--paper);
}
.blog-card-media.has-img { background: var(--paper2); }
.blog-card-media .hc-img { transition: transform .5s var(--ease); }
.blog-card:hover .blog-card-media .hc-img { transform: scale(1.04); }
.blog-card-media__label {
  display: flex; width: 100%; height: 100%;
  align-items: center; justify-content: center;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .08em; color: var(--sub);
}

.blog-card__body { display: flex; flex-direction: column; gap: 10px; padding: var(--pad-card-sm); flex: 1; }
.blog-card__meta {
  display: flex; gap: 10px; align-items: center;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--bronze);
}
.blog-card__sep  { opacity: .45; }
.blog-card__date { color: var(--sub); }
.blog-card__title { font-size: 19px; font-weight: 800; letter-spacing: -.01em; line-height: 1.25; }
.blog-card--scroll .blog-card__title { font-size: 17px; }
.blog-card__excerpt { font-size: 14px; line-height: 1.55; color: var(--sub); }
.blog-card-cta {
  margin-top: auto; padding-top: 12px;
  font-family: var(--font-mono); font-size: var(--label); color: var(--blue);
}

/* -- Outreach: pillars ----------------------------------------------------- */
.pillar-tabs { display: flex; flex-direction: column; gap: 8px; }
.pillar-tab {
  display: flex; align-items: center; gap: 16px;
  width: 100%;
  cursor: pointer; text-align: left;
  padding: 18px 20px;
  border-radius: var(--radius-surface);
  border: 1px solid var(--line);
  background: transparent;
  font-family: var(--font-sans);
  color: inherit;
  transition: border-color var(--t-fast), background var(--t-fast);
}
.pillar-tab.is-active { border-color: var(--accent); background: var(--paper); }
.pillar-num {
  font-family: var(--font-mono);
  font-size: var(--label-lg); font-weight: 600;
  color: var(--muted-2); min-width: 22px;
  transition: color var(--t-fast);
}
.pillar-tab.is-active .pillar-num { color: var(--accent); }
.pillar-tab__title { font-size: 18px; font-weight: 800; letter-spacing: -.01em; }

.pillar-panel {
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: var(--stripe) solid var(--accent);
  border-radius: var(--radius-surface);
  padding: var(--pad-card-lg);
  min-height: 320px;
}
.pillar-body { display: none; }
.pillar-body.is-active { display: block; }
.pillar-body__lede { font-size: 19px; line-height: 1.6; color: var(--sub); margin: 18px 0 0; max-width: 52ch; }

/* -- Outreach: mission / vision / aim -------------------------------------- */
/* Each card is a photo header that doubles as the toggle, over a statement that
   always reads, over a panel of detail that opens. Deliberately a different
   interaction from the pillar tabs below — same page, so the two sections
   should not look like the same widget twice. */
.aimset { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; align-items: start; }

.aim {
  background: var(--paper);
  border: 1px solid var(--line);
  border-top: var(--stripe) solid var(--accent);
  border-radius: var(--radius-surface);
  overflow: hidden;
  transition: transform var(--t-base) var(--ease), box-shadow var(--t-base);
}
.aim:hover,
.aim.is-open { box-shadow: var(--shadow-card); }
.aim:hover { transform: translateY(-4px); }

/* Head — the whole photo block is the button */
.aim__head {
  position: relative; display: block; width: 100%;
  padding: 0; border: 0; background: none;
  font-family: var(--font-sans); color: inherit; text-align: left;
  cursor: pointer;
}
.aim__photo {
  display: block; height: 176px;
  background-color: var(--paper2);
  background-position: center; background-size: cover; background-repeat: no-repeat;
  transition: transform .5s var(--ease);
}
.aim:hover .aim__photo { transform: scale(1.04); }

/* Scrim keeps the label legible whatever the photo underneath is doing */
.aim__head::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(0deg, rgba(27,23,18,.80) 0%, rgba(27,23,18,.10) 62%, rgba(27,23,18,.28) 100%);
  pointer-events: none;
}
.aim__badge {
  position: absolute; top: 14px; left: 14px; z-index: 2;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--accent); color: var(--paper);
  display: flex; align-items: center; justify-content: center;
  transition: transform var(--t-base) var(--ease);
}
.aim__badge svg { width: 20px; height: 20px; }
.aim:hover .aim__badge { transform: scale(1.08); }

.aim__label {
  position: absolute; left: 18px; bottom: 15px; z-index: 2;
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--paper);
}
.aim__chev {
  position: absolute; right: 16px; bottom: 12px; z-index: 2;
  width: 28px; height: 28px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.55);
  display: flex; align-items: center; justify-content: center;
  transition: background var(--t-fast), border-color var(--t-fast);
}
.aim__chev::before {
  content: ''; width: 7px; height: 7px; margin-top: -3px;
  border-right: 1.5px solid var(--paper); border-bottom: 1.5px solid var(--paper);
  transform: rotate(45deg);
  transition: transform var(--t-base) var(--ease);
}
.aim.is-open .aim__chev::before { transform: rotate(-135deg); margin-top: 3px; }
.aim__head:hover .aim__chev { background: rgba(255,255,255,.16); border-color: var(--paper); }
.aim__head:focus-visible .aim__chev { background: rgba(255,255,255,.22); border-color: var(--paper); }

.aim__body { padding: var(--pad-card-sm); }
.aim__statement {
  margin: 0;
  font-size: 17px; line-height: 1.55; font-weight: 600; letter-spacing: -.01em;
}

/* 0fr → 1fr animates the panel open without hard-coding a max-height that would
   have to be re-guessed every time the copy changes. */
.aim__panel {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--t-slow) var(--ease);
}
.aim.is-open .aim__panel { grid-template-rows: 1fr; }
.aim__panel > * { overflow: hidden; }

/* All three start closed. On a real pointer, hovering opens a card without
   asking for a click; the click handler stays for touch and keyboard, where
   there is no hover to rely on. Gated behind hover:hover so a tap on a phone
   does not leave a card stuck open under a lingering :hover. */
@media (hover: hover) and (pointer: fine) {
  .aim:hover .aim__panel { grid-template-rows: 1fr; }
  .aim:hover .aim__chev::before { transform: rotate(-135deg); margin-top: 3px; }
}

.aim__points { list-style: none; margin: 0; padding: 18px 0 0; }
.aim__points li {
  position: relative; padding-left: 20px;
  font-size: 14.5px; line-height: 1.55; color: var(--sub);
}
.aim__points li + li { margin-top: 11px; }
.aim__points li::before {
  content: ''; position: absolute; left: 0; top: 8px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
}

/* -- Outreach: programs ---------------------------------------------------- */
.prog-card {
  cursor: pointer;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  overflow: hidden;
}
.prog-card__head {
  position: relative; height: 170px;
  background:
    repeating-linear-gradient(45deg, rgba(255,255,255,.12) 0 10px, transparent 10px 20px),
    var(--prog-bg, var(--green));
}
.prog-card__sign {
  position: absolute; bottom: 14px; right: 18px;
  font-family: var(--font-mono);
  font-size: 30px; font-weight: 600;
  color: rgba(243,237,225,.9);
}
.prog-card__body { padding: var(--pad-card); }
.prog-card__title { font-size: 24px; font-weight: 800; letter-spacing: -.01em; margin: 0; }
.prog-body {
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height .4s ease, opacity .4s ease;
}
.prog-card.is-open .prog-body { max-height: 240px; opacity: 1; }
.prog-body p { font-size: 15.5px; line-height: 1.6; color: var(--sub); margin: 12px 0 0; }

/* -- Outreach: video ------------------------------------------------------- */
.video-frame {
  border-radius: var(--radius-surface);
  overflow: hidden;
  border: 1px solid rgba(255,255,255,.16);
  box-shadow: 0 24px 60px rgba(0,0,0,.4);
  background: #000;
  aspect-ratio: 16/9;
}
.video-frame iframe { width: 100%; height: 100%; border: 0; display: block; }

/* -- Outreach: activity cards ---------------------------------------------- */
/* Slides size to the tallest card so every tile in the row matches, and the
   media strip stays a fixed height so all photos crop to the same box. */
.act-swiper .swiper-slide { height: auto; }

.act-card {
  text-decoration: none;
  display: flex;
  flex-direction: column;
  height: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  overflow: hidden;
  background: var(--paper2);
}
.act-card__media {
  position: relative;
  height: 190px;
  flex: none;
  background:
    repeating-linear-gradient(45deg, rgba(255,255,255,.12) 0 10px, transparent 10px 20px),
    var(--act-bg, var(--green));
}
.act-card__media.has-img { background: var(--paper2); }
.act-card__play {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: 56px; height: 56px; border-radius: 50%;
  background: rgba(255,255,255,.92); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; padding-left: 4px;
}
.act-card__body { padding: var(--pad-card-sm); flex: 1; display: flex; flex-direction: column; }
.act-card__title { font-size: 20px; font-weight: 800; letter-spacing: -.01em; margin: 10px 0 0; line-height: 1.2; color: var(--ink); }
.act-card__desc { font-size: 14px; line-height: 1.55; color: var(--sub); margin: 8px 0 0; }
.act-card__cta { display: inline-block; margin-top: auto; padding-top: 14px; font-family: var(--font-mono); font-size: var(--label); color: var(--accent); }

/* -- Outreach: events ------------------------------------------------------ */
.event-row {
  text-decoration: none; color: var(--ink);
  display: grid;
  grid-template-columns: 130px 1fr auto;
  gap: var(--gap-md);
  align-items: center;
  padding: var(--pad-card);
  border: 1px solid var(--line);
  background: var(--paper2);
  border-radius: var(--radius-surface);
  margin-bottom: 8px;
}
.event-row__date { text-align: center; border-right: 1px solid var(--line); }
.event-row__day { font-size: 38px; font-weight: 900; letter-spacing: -.02em; color: var(--accent); line-height: 1; }
.event-row__mon {
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--sub); margin-top: 4px;
}
.event-row__title { font-size: 22px; font-weight: 800; letter-spacing: -.01em; margin: 0; }
.event-row__meta { font-family: var(--font-mono); font-size: var(--label); letter-spacing: .04em; color: var(--sub); margin-top: 6px; }
.event-row__arrow { font-family: var(--font-mono); font-size: var(--label-lg); color: var(--accent); }

/* -- Outreach: socials ----------------------------------------------------- */
.social-list { display: grid; gap: 12px; }
.social-row {
  text-decoration: none;
  display: flex; align-items: center; gap: 14px;
  padding: 18px 20px;
  border: 1px solid rgba(255,255,255,.3);
  border-radius: var(--radius-surface);
  color: var(--on-green);
}
.social-row__icon {
  width: 38px; height: 38px;
  border-radius: var(--radius-surface);
  background: rgba(255,255,255,.14);
  display: flex; align-items: center; justify-content: center;
  flex: none;
}
.social-row__name { font-size: 17px; font-weight: 800; }
.social-row__out { margin-left: auto; font-family: var(--font-mono); font-size: 15px; color: var(--gold-text); }

/* -- Contact --------------------------------------------------------------- */
.reason-grid { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 22px; }
.reason {
  font-family: var(--font-sans);
  font-size: 15px; font-weight: 700;
  cursor: pointer;
  padding: 12px 20px;
  border-radius: var(--radius-control);
  border: 1px solid var(--line);
  background: var(--paper2);
  color: var(--ink);
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.reason.is-active { background: var(--blue); color: var(--paper2); border-color: var(--blue); }

.regarding {
  display: flex; align-items: center; gap: 12px;
  margin: 20px 0 24px;
  background: var(--paper2);
  border: 1px solid var(--line);
  border-left: var(--stripe) solid var(--blue);
  border-radius: var(--radius-surface);
  padding: 14px 18px;
}
.regarding__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--sub);
}
.regarding__value { font-size: 16px; font-weight: 800; color: var(--blue); }

.form-status { display: none; margin-top: 16px; font-family: var(--font-mono); font-size: var(--label-lg); }
.form-status.is-ok  { display: block; color: var(--greenDeep); }
.form-status.is-err { display: block; color: var(--rust); }

.channel {
  text-decoration: none; color: var(--ink);
  display: block;
  padding: 20px 0;
  border-top: 1px solid var(--line);
}
.channel:last-child { border-bottom: 1px solid var(--line); }
.channel__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--sub); margin-bottom: 6px;
}
.channel__value { font-size: 19px; font-weight: 800; letter-spacing: -.01em; }

/* -- Writing: in-development callout --------------------------------------- */
.callout__title { font-size: 32px; font-weight: 900; letter-spacing: -.02em; margin: 0; max-width: 22ch; }
.callout__body { font-size: 16px; line-height: 1.6; color: var(--sub); margin: 14px 0 0; max-width: 56ch; }


/* -- Outreach: the problem / pain-point band -------------------------------- */
.factgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap); margin-top: 38px; }
.fact { border-top: var(--stripe) solid var(--gold); padding-top: 20px; }
.fact__figure {
  font-size: clamp(36px, 4.2vw, 54px);
  font-weight: 900; letter-spacing: -.03em; line-height: 1;
  color: var(--gold);
}
.fact__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--band-fg); opacity: .7;
  margin-top: 12px;
}
.fact__body { font-size: 15.5px; line-height: 1.55; color: var(--fade-soft); margin: 10px 0 0; }
.source-note {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .04em;
  color: var(--fade-weak);
  margin: 30px 0 0;
}

/* -- Embedded media (YouTube, LinkedIn posts) ------------------------------- */
.embed {
  position: relative;
  width: 100%;
  border-radius: var(--radius-surface);
  overflow: hidden;
  border: 1px solid var(--line);
  background: #000;
}
.embed--16-9 { aspect-ratio: 16/9; }
.embed--post { aspect-ratio: 504/399; background: var(--paper2); }
.embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }

/* -- Outreach: program feature sections ------------------------------------ */
.feature__media { display: flex; flex-direction: column; gap: 16px; }

/* Every lead media box in a feature renders at one size — full column width on
   a single 16/9 box — so a YouTube embed, a LinkedIn post and a still photo all
   occupy the same footprint instead of each following its own source ratio.
   Stills letterbox against the paper surface rather than being cropped, except
   where a feature opts into fit-cover. */
.feature__media .embed,
.feature__media .media { width: 100%; aspect-ratio: 16/9; }
.feature__num {
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--accent);
}

/* The copy column owns its own rhythm rather than each child bringing its own
   margin, so heading → prose → prose → links all sit on one even step no
   matter which optional pieces (subtitle, extra paragraphs, links) a feature
   uses. The kicker and deck line are the two deliberate exceptions: they read
   as part of the title block, so they sit tight against it. */
.feature__copy > * + *        { margin-top: 20px; }
.feature__copy > .h-section   { margin-top: 12px; }
.feature__copy > .subtitle    { margin-top: 8px;  }
.feature__copy > :first-child { margin-top: 0;    }

/* Thumbnail strip — click to open in the lightbox */
.thumbstrip { display: flex; gap: 12px; flex-wrap: wrap; }
.thumb {
  position: relative;
  width: 148px; height: 111px;
  border-radius: var(--radius-surface);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--paper2);
  display: block;
  cursor: zoom-in;
  transition: transform var(--t-base) var(--ease), box-shadow var(--t-base);
}
.thumb:hover { transform: translateY(-3px); box-shadow: var(--shadow-card); }
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* UI screenshots and covers must not be cropped — show them whole */
.thumb--contain img { object-fit: contain; padding: 6px; }

/* A media box that opens full size */
.media--zoom { cursor: zoom-in; }

/* -- Donate actions -------------------------------------------------------- */
/* Direct-giving buttons ship hidden and appear as soon as a href is filled
   into data/outreach.json → donate.direct[].href */
.donate-actions { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 30px; }

/* -- Outreach: page-scoped colour ------------------------------------------
   Sections theme themselves through .band-- / .accent-- classes, but a few
   shared components still name a colour outright — blog cards were drawn for
   the blue-accented pages they normally sit on, and the elevation tokens carry
   a blue cast. Rebinding them under the page's data-page attribute keeps the
   Outreach page one green family without repainting Writing or Blog. */
body[data-page="outreach"] {
  --shadow-card: 0 16px 34px rgba(22,40,31,.16);
  --shadow-lift: 0 18px 40px rgba(22,40,31,.20);
  --shadow-soft: 0 12px 28px rgba(22,40,31,.12);
}
body[data-page="outreach"] .blog-card:hover     { border-color: var(--green); }
body[data-page="outreach"] .blog-card__meta     { color: var(--greenDeep); }
body[data-page="outreach"] .blog-card-cta       { color: var(--green); }
body[data-page="outreach"] .blog-scroll::-webkit-scrollbar-thumb:hover { background: var(--green); }
/* The scrim over the aim photos is warm ink everywhere else; here it is the
   darkest green, so a photo header cools into the band instead of the page. */
body[data-page="outreach"] .aim__head::after {
  background: linear-gradient(0deg, rgba(22,40,31,.82) 0%, rgba(22,40,31,.10) 62%, rgba(22,40,31,.30) 100%);
}

/* -- Consulting: practitioner hero ----------------------------------------- */
/* The advisory read: headline on the left, who you would actually be hiring on
   the right. The portrait is a 184px source, so the frame is capped just under
   it — scaling it up would only make it soft. */
.hero-consult {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 316px;
  gap: var(--gap-lg);
  align-items: center;
}

.who {
  background: var(--hair-lighter);
  border: 1px solid var(--hair-light);
  border-radius: var(--radius-surface);
  padding: var(--pad-card);
  text-align: center;
}
.who__portrait {
  width: 148px; height: 148px;
  margin: 0 auto;
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid var(--gold);
  background: var(--paper2);
  box-shadow: 0 14px 30px rgba(0,0,0,.3);
}
.who__portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }

.who__name { font-size: 23px; font-weight: 900; letter-spacing: -.02em; margin-top: 18px; }
.who__role {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--gold-text);
  margin-top: 7px;
}
.who__facts {
  list-style: none;
  margin: 20px 0 0; padding: 18px 0 0;
  border-top: 1px solid var(--hair-light);
  display: grid; gap: 11px;
  text-align: left;
}
.who__facts li {
  position: relative;
  padding-left: 20px;
  font-size: 13.5px; line-height: 1.5;
  color: var(--fade);
}
.who__facts li::before {
  content: "";
  position: absolute; left: 0; top: .48em;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--gold);
}

/* Small round portrait over the closing CTA — the page ends on the person */
.cta-avatar {
  width: 78px; height: 78px;
  margin: 0 auto 24px;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid var(--gold);
}
.cta-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* -- Consulting: on-this-page nav ------------------------------------------ */
/* The site header does not stick, so this bar owns the top of the viewport once
   the hero scrolls away. It is plain anchors: it works before enhance.js loads,
   and the script only adds the aria-current that tracks the reader's position. */
/* The bar is the same near-black on every page -- it and the announcement strip
   and the footer are one continuous chrome, and the coloured banner sits
   between them. Everything on it inverts from the light version it used to be:
   the label and the resting links go to a dimmed paper rather than --sub, which
   measures 3.3:1 on this ground and would fail. */
.pagenav {
  position: sticky; top: 0; z-index: 80;
  background: var(--chrome);
  color: var(--paper2);
  border-bottom: 1px solid rgba(255,255,255,.10);
}
.pagenav__inner {
  max-width: var(--wrap-max);
  margin: 0 auto;
  padding: 0 var(--wrap-x);
  min-height: 58px;
  display: flex; align-items: center; gap: 22px;
}
.pagenav__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  color: rgba(251,247,238,.58); white-space: nowrap;
}
.pagenav__list {
  list-style: none; margin: 0; padding: 0;
  /* min-width:0 is what actually lets this flex item scroll — without it the
     item floors at its content width and pushes the whole page sideways. */
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 2px;
  /* The row scrolls sideways on narrow screens rather than wrapping the bar
     onto a second line and stealing viewport height from the content. */
  overflow-x: auto;
  scrollbar-width: none;
}
.pagenav__list::-webkit-scrollbar { display: none; }
.pagenav__link {
  display: block; white-space: nowrap;
  padding: 9px 12px;
  font-size: 13.5px; font-weight: 600;
  text-decoration: none;
  /* 8.9:1 at rest, so a link is readable before it is the current one. */
  color: rgba(251,247,238,.72);
  border-bottom: 2px solid transparent;
  transition: color var(--t-fast), background var(--t-fast);
}
.pagenav__link:hover { color: var(--paper2); background: rgba(255,255,255,.07); }
/* Only the current-section mark and the CTA carry the page's own hue.

   Each page uses the BRIGHT end of its colour here, not the deep end it uses
   elsewhere. On the old paper bar the deep versions were the readable ones; on
   this ground they collapse -- blue-hi to 2.3:1, the deep green to 2.2:1, the
   olive to 2.3:1. Every value below clears 5.5:1 as text on the chrome, and
   the CTA reverses it: chrome text on the accent as a fill. */
.pagenav__link[aria-current="true"] { color: var(--pagenav-accent, var(--gold)); border-bottom-color: var(--pagenav-accent, var(--gold)); }
body[data-page="consulting"]  .pagenav { --pagenav-accent: var(--gold);       }  /* 8.9:1 */
body[data-page="application"] .pagenav { --pagenav-accent: var(--tealBright); }  /* 8.2:1 */
body[data-page="outreach"]    .pagenav { --pagenav-accent: #5C9878;           }  /* 5.6:1 */
body[data-page="writing"]     .pagenav { --pagenav-accent: var(--w-olive-mid);}  /* 8.2:1 */
/* Matches the page's own blue rather than the darker site-wide .btn--ink */
/* The fill is now a bright colour, so the label on it has to be dark. It was
   .btn--ink (paper text on near-black) when the bar was paper. */
.pagenav__cta {
  margin-left: auto;
  background: var(--pagenav-accent, var(--gold));
  color: var(--chrome);
  border-color: transparent;
}
.pagenav__cta:hover { filter: brightness(1.08); }

/* Anchored sections clear the sticky bar when jumped to */
[data-page="consulting"] .band[id] { scroll-margin-top: 66px; }

/* -- Consulting: engagement cards ------------------------------------------ */
/* The paragraph is always visible; the disclosure only re-states those same
   facts as a scannable list. Native <details>, so it opens without JavaScript
   and screen readers get the expanded/collapsed state for free. */
.ecard { display: flex; flex-direction: column; }
.ecard .note { flex: 1; }
.ecard--paper { background: var(--paper); }

/* Icon and index share the card's top line — the icon names the kind of work
   at a glance, the numeral keeps the reading order. */
.ecard__head { display: flex; align-items: center; gap: var(--gap-xs); }
.ecard__icon {
  flex: none;
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: var(--radius-surface);
  color: var(--accent);
  background: rgba(27,23,18,.06);                              /* fallback */
  background: color-mix(in srgb, var(--accent) 11%, transparent);
  transition: transform var(--t-base) var(--ease), background var(--t-base);
}
.ecard__icon svg { width: 22px; height: 22px; }
.ecard:hover .ecard__icon {
  transform: translateY(-2px) scale(1.06);
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}
.ecard .card__index { line-height: 1; }

.disclose { margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--line); }
.disclose__summary {
  display: flex; align-items: center; gap: 10px;
  padding: 4px 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--accent);
}
.disclose__summary::-webkit-details-marker { display: none; }
.disclose__summary:hover .disclose__icon { background: rgba(27,23,18,.07); }

/* Uses the shared chevron below rather than a plus-becomes-minus of its own.
   The site had three ways of saying "this opens": a plus here, a chevron on the
   Outreach aims, a native arrow on the Writing filter. A reader learns one. */
.disclose__icon {
  position: relative; flex: none;
  width: 18px; height: 18px;
  border: 1px solid currentColor; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  transition: background var(--t-fast);
}
.disclose__icon::before {
  content: ""; width: 5px; height: 5px; margin-top: -2px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--t-base) var(--ease);
}
.disclose[open] .disclose__icon::before { transform: rotate(-135deg); margin-top: 2px; }

.disclose__panel { padding-top: 14px; animation: fadeUp .35s var(--ease) both; }

/* -- Consulting: at-a-glance fact list ------------------------------------- */
.glance { margin: 0; display: grid; gap: var(--gap-xs); }
.glance__row { display: grid; grid-template-columns: 108px 1fr; gap: 14px; align-items: baseline; }
.glance__term {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--sub);
}
.glance__desc { margin: 0; font-size: 14.5px; line-height: 1.55; color: var(--prose); }

/* Ruled variant — rows separated rather than spaced, for the longer case lists.
   Colours come from the band, so the list reads correctly on paper or on the
   blue foresight band without a second set of rules. */
.glance--ruled { gap: 0; }
.glance--ruled .glance__row { padding: 13px 0; border-top: 1px solid var(--band-hair); }
.glance--ruled .glance__term { color: var(--accent); }
.glance--ruled .glance__desc { color: var(--band-fg); opacity: .84; }

/* -- Consulting: market foresight cases ------------------------------------ */
/* Both cases and both documents ship visible and stacked. enhance.js adds
   .is-tabbed and unhides the tab bar and pager, so with JavaScript off the
   reader still reaches every case — the controls are an upgrade, not the only
   route in. Everything here is drawn from --band-*, so the block survives being
   moved to a different coloured band. */
.cases {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
  gap: var(--gap-lg);
  align-items: start;
}
.cases__stage { display: grid; gap: 26px; }

.cases__nav { display: flex; align-items: center; gap: 12px; }
.cases__count { color: var(--band-fg); opacity: .7; white-space: nowrap; }

/* Carousel buttons on a coloured band */
.nav-btn--onband { border-color: var(--band-fg); color: var(--band-fg); }
.nav-btn--onband:hover { background: var(--band-fg); color: var(--band-bg); }

.artifact { margin: 0; }
.cases.is-tabbed .artifact { display: none; }
.cases.is-tabbed .artifact.is-active { display: block; animation: caseIn .45s var(--ease) both; }

.artifact__frame {
  position: relative; display: block;
  border: 1px solid var(--band-hair);
  border-radius: var(--radius-surface);
  overflow: hidden;
  background: var(--paper2);
  box-shadow: 0 20px 44px rgba(0,0,0,.26);
  cursor: zoom-in;
  transition: transform var(--t-base) var(--ease), box-shadow var(--t-base);
}
.artifact__frame:hover, .artifact__frame:focus-visible { opacity: 1; }
.artifact__frame:hover { transform: translateY(-4px); box-shadow: 0 26px 54px rgba(0,0,0,.34); }
.artifact__frame img { display: block; width: 100%; height: auto; transition: transform .5s var(--ease); }
.artifact__frame:hover img { transform: scale(1.02); }

.artifact__zoom {
  position: absolute; left: 50%; bottom: 16px;
  transform: translateX(-50%) translateY(6px);
  padding: 8px 15px;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .08em;
  color: var(--paper);
  background: rgba(27,23,18,.8);
  border-radius: var(--radius-pill);
  opacity: 0;
  transition: opacity var(--t-fast), transform var(--t-fast) var(--ease);
}
.artifact__frame:hover .artifact__zoom,
.artifact__frame:focus-visible .artifact__zoom { opacity: 1; transform: translateX(-50%) translateY(0); }

.artifact__cap {
  font-family: var(--font-mono);
  font-size: var(--label-sm); line-height: 1.6; letter-spacing: .04em;
  color: var(--band-fg); opacity: .68;
  margin: 12px 0 0;
}

.cases__tabs { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 28px; }
.case-tab {
  display: flex; align-items: center; gap: 13px;
  text-align: left;
  padding: 12px 18px;
  background: transparent;
  border: 1px solid var(--band-hair);
  border-radius: var(--radius-surface);
  color: var(--band-fg);
  font-family: var(--font-sans);
  cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast), transform var(--t-fast) var(--ease);
}
.case-tab:hover { border-color: var(--accent); transform: translateY(-2px); }
/* Filled pill follows the site's gold-on-ink button pairing, which holds for
   any dark band — every one of them resolves --accent to gold. */
.case-tab.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
  transform: none;
}
.case-tab__icon { flex: none; display: grid; place-items: center; opacity: .8; }
.case-tab__icon svg { width: 22px; height: 22px; }
.case-tab.is-active .case-tab__icon { opacity: 1; }
.case-tab__text   { display: grid; gap: 3px; }
.case-tab__year   { font-family: var(--font-mono); font-size: var(--label-sm); letter-spacing: .12em; opacity: .72; }
.case-tab__name   { font-size: 19px; font-weight: 800; letter-spacing: -.01em; }
.case-tab__kicker { font-size: 12.5px; opacity: .82; }

.case { max-width: 62ch; }
.case + .case { margin-top: 40px; padding-top: 40px; border-top: 1px solid var(--band-hair); }
.cases.is-tabbed .case { display: none; margin-top: 0; padding-top: 0; border-top: 0; }
.cases.is-tabbed .case.is-active { display: block; }
.case__kicker { color: var(--accent); }
.case__body { font-size: 16.5px; line-height: 1.7; color: var(--band-fg); opacity: .86; margin: 18px 0 0; }
.case .glance { margin-top: 26px; }

/* The swapped-in case assembles rather than appearing all at once: kicker,
   title, paragraph and fact list each land a beat apart. Four children, four
   delays — the reduced-motion block collapses them all to nothing. */
.cases.is-tabbed .case.is-active > * { animation: caseIn .45s var(--ease) both; }
.cases.is-tabbed .case.is-active > :nth-child(1) { animation-delay: .02s; }
.cases.is-tabbed .case.is-active > :nth-child(2) { animation-delay: .07s; }
.cases.is-tabbed .case.is-active > :nth-child(3) { animation-delay: .12s; }
.cases.is-tabbed .case.is-active > :nth-child(4) { animation-delay: .17s; }

/* -- Consulting: how-to-engage flow ---------------------------------------- */
.steps--flow .steps__item { transition: transform var(--t-base) var(--ease); }
.steps--flow .steps__item:hover { transform: translateY(-3px); }

/* ============================================================================
   APPLICATIONS — the night page
   ----------------------------------------------------------------------------
   The one page that runs dark end to end. It is about software, so it borrows
   software's own vocabulary: a night surface, a drifting code grid, browser
   chrome around every screenshot, mono for anything a machine would print.

   Everything below is either scoped to a class this page alone uses, or to
   [data-page="application"] — no other page changes because this file grew.
   ============================================================================ */

/* -- Ambient background: grid + glow --------------------------------------- */
/* Both are decorative and pointer-transparent, so they never sit between the
   reader and a link. The grid is masked to fade out before it reaches the copy
   rather than running edge to edge behind the text. */
.codegrid {
  position: absolute; inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--hair-night) 1px, transparent 1px),
    linear-gradient(90deg, var(--hair-night) 1px, transparent 1px);
  background-size: 62px 62px;
  opacity: .55;
  -webkit-mask-image: radial-gradient(120% 78% at 22% 0%, #000 8%, transparent 72%);
          mask-image: radial-gradient(120% 78% at 22% 0%, #000 8%, transparent 72%);
  animation: gridDrift 34s linear infinite;
}
.codegrid--soft { opacity: .3; animation-duration: 52s; }

.glow {
  position: absolute;
  pointer-events: none;
  width: 46vw; height: 46vw;
  max-width: 640px; max-height: 640px;
  border-radius: 50%;
  filter: blur(10px);
  opacity: .5;
}
.glow--a { top: -16%;  right: -6%;  background: radial-gradient(circle, rgba(47,191,163,.26) 0%, transparent 66%); animation: floatGlow 17s var(--ease) infinite; }
.glow--b { bottom: -28%; left: -12%; background: radial-gradient(circle, rgba(94,76,122,.34) 0%, transparent 66%); animation: floatGlow 23s var(--ease) infinite reverse; }
.glow--c { top: 4%; left: 42%; background: radial-gradient(circle, rgba(217,172,91,.16) 0%, transparent 66%); animation: floatGlow 29s var(--ease) infinite; }

/* -- Hero ------------------------------------------------------------------ */
.apphero, .appwork, .eras-band, .appcta { position: relative; overflow: hidden; }
/* The content column sits above the ambience in every night section — the grid
   and the glows are painted behind it, never over a link. */
.apphero__wrap,
.appwork > .wrap,
.eras-band > .wrap,
.appcta > .wrap { position: relative; z-index: 1; }
.apphero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr);
  gap: var(--gap-lg);
  align-items: center;
}
/* Each headline line is its own .riseWrap and must not wrap inside one, so the
   type is set to the longest of the three lines rather than to the usual hero
   scale — the copy column here is only half the page wide. */
.apphero .h-hero { font-size: var(--step-5); line-height: 1.06; max-width: 22ch; }
.apphero .hero-lede { border-top-color: var(--hair-night); }

/* Hero counters — the numerals count up once the block is on screen (JS), and
   simply read as their final value when it is not available. */
.hstats {
  display: grid; grid-template-columns: repeat(3, auto);
  justify-content: start;
  gap: 44px;
  margin: 40px 0 0; padding-top: 26px;
  border-top: 1px solid var(--hair-night);
}

/* The Applications figures, once they moved out of the hero to let every banner
   share one height. Spread across the strip rather than bunched at the left,
   which is how the Outreach strip one page over already reads.

   This has to sit AFTER .hstats, not before it. Both selectors carry the same
   specificity, so the later rule wins -- and while this lived up in the banner
   section it was being silently overridden by the base rule 1300 lines below,
   which is why the figures stayed bunched at the left with uneven gaps. */
/* No rule and even air. In the hero the border separated the figures from the
   buttons above them; as its own strip the band edges already do that, and the
   inherited 26px top pad against 0 bottom made the row sit high in its band. */
.hstats--strip {
  grid-template-columns: repeat(3, 1fr);
  justify-content: stretch;
  margin: 0;
  padding: 0;
  border-top: 0;
}
.hstat__num {
  font-size: clamp(30px, 3.4vw, 40px);
  font-weight: 900; letter-spacing: -.03em; line-height: 1;
  color: var(--tealBright);
  font-variant-numeric: tabular-nums;
}
.hstat__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--fade-night); opacity: .72;
  margin: 10px 0 0;
}

/* -- Terminal panel -------------------------------------------------------- */
.terminal {
  background: rgba(6,20,27,.68);
  border: 1px solid var(--hair-night);
  border-radius: var(--radius-surface);
  box-shadow: 0 34px 80px rgba(0,0,0,.55);
  overflow: hidden;
}
.terminal__bar {
  display: flex; align-items: center; gap: 8px;
  padding: 13px 16px;
  background: rgba(255,255,255,.04);
  border-bottom: 1px solid var(--hair-night);
}
.terminal__dot, .shot__dot { width: 11px; height: 11px; border-radius: 50%; flex: none; }
.terminal__dot--r, .shot__dot--r { background: #FF5F57; }
.terminal__dot--y, .shot__dot--y { background: #FEBC2E; }
.terminal__dot--g, .shot__dot--g { background: #28C840; }
.terminal__name {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .08em;
  color: var(--fade-night); opacity: .55;
  margin-left: 10px;
}
.terminal__body {
  padding: 22px 22px 26px;
  font-family: var(--font-mono);
  font-size: var(--label-lg); line-height: 2;
  min-height: 268px;
}
.terminal__line { margin: 0; white-space: pre-wrap; color: var(--on-night); }
.terminal__line--out { color: var(--fade-night); opacity: .68; padding-left: 16px; }
.terminal__prompt { color: var(--tealBright); margin-right: 8px; }
/* While the panel is being typed out, lines that have not been reached yet are
   held back rather than removed, so nothing reflows as each one lands. */
.terminal.is-typing .terminal__line { visibility: hidden; }
.terminal.is-typing .terminal__line.is-typed { visibility: visible; }

/* -- Filter bar ------------------------------------------------------------ */
.filterbar {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-top: 34px; padding-top: 26px;
  border-top: 1px solid var(--hair-night);
}
.filterbar__label {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--fade-night); opacity: .6;
}
.filterbar__set { display: flex; gap: 8px; flex-wrap: wrap; }
.filter {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px;
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--fade-night);
  background: transparent;
  border: 1px solid var(--hair-night);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast);
}
.filter:hover { color: var(--on-night); border-color: rgba(47,191,163,.55); }
.filter.is-active { background: var(--tealBright); border-color: var(--tealBright); color: #05221C; }
/* The count beside a filter name. 10px at 60% opacity was below the legible
   floor twice over; it now sits at the label floor and carries its contrast
   in colour rather than by fading the whole glyph. */
.filter__n { font-size: var(--label-sm); color: var(--fade-night); }
.filterbar__count {
  margin: 0 0 0 auto;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .06em;
  color: var(--fade-night); opacity: .55;
}

/* -- Application grid ------------------------------------------------------ */
.appgrid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 26px;
  margin-top: 32px;
}
.appgrid__empty {
  font-family: var(--font-mono); font-size: var(--label-lg);
  color: var(--fade-night); opacity: .6;
  margin: 30px 0 0;
}

.appcard {
  position: relative;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.045));
  border: 1px solid var(--hair-night);
  border-radius: var(--radius-surface);
  overflow: hidden;
  /* One transform chain, driven entirely by custom properties, so the three
     things that move a card never fight over the same declaration: the filter's
     FLIP glide (--fx/--fy), the pointer tilt (--rx/--ry) and the hover lift
     (--ty) each own their own slot and compose here. */
  transform: perspective(900px)
             translate3d(var(--fx, 0px), var(--fy, 0px), 0)
             rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg))
             translateY(var(--ty, 0px));
  transition: transform var(--t-slow) var(--ease), border-color var(--t-base), box-shadow var(--t-slow);
}
/* While the pointer is on a card the tilt has to feel attached to the cursor,
   not eased behind it; while FLIP is setting the start offset there must be no
   transition at all or the card would animate to its own starting point. */
.appcard.is-tilting { transition: transform .12s linear, border-color var(--t-base), box-shadow var(--t-slow); }
.appcard.is-flipping { transition: none; }

/* Pointer spotlight — sits above the card surface and below its content, so it
   lights the card without washing out the copy. */
.appcard::after {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 0%), rgba(47,191,163,.16), transparent 62%);
  opacity: 0;
  transition: opacity var(--t-base);
  pointer-events: none;
}
.appcard.is-lit::after { opacity: 1; }
.appcard > * { position: relative; z-index: 1; }
/* A hairline that lights along the top edge on hover — the card's "power on" */
.appcard::before {
  content: "";
  position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--tealBright), var(--gold));
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform var(--t-slow) var(--ease);
  z-index: 3;
}
.appcard:hover, .appcard:focus-within {
  --ty: -6px;
  border-color: rgba(47,191,163,.42);
  box-shadow: 0 26px 60px rgba(0,0,0,.5);
}
.appcard:hover::before, .appcard:focus-within::before { transform: scaleX(1); }
.appcard[hidden] { display: none; }
.appcard.is-entering { animation: fadeUp .45s var(--ease) both; animation-delay: calc(var(--i, 0) * 45ms); }

/* Screenshot in browser chrome ------------------------------------------- */
.shot { position: relative; background: var(--night-3); border-bottom: 1px solid var(--hair-night); }
.shot__bar {
  display: flex; align-items: center; gap: 7px;
  padding: 10px 14px;
  background: rgba(0,0,0,.20);
}
.shot__url {
  flex: 1; min-width: 0;
  margin-left: 8px; padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,.06);
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .04em;
  color: var(--fade-night); opacity: .62;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.shot__img {
  display: block; position: relative;
  aspect-ratio: 16/10;
  overflow: hidden;
  background: var(--night-3);
  cursor: zoom-in;
}
.shot__img img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
  display: block;
  transition: transform .8s var(--ease);
}
.appcard:hover .shot__img img { transform: scale(1.05); }
.shot__img:hover, .shot__img:focus-visible { opacity: 1; }

/* Phone mock-ups and desktop UI shots are the wrong shape for a 16:10 crop —
   cropping them throws away the very thing the card is showing. These sit whole
   inside the frame instead, letterboxed against the chrome surface. */
.shot__img--contain img { object-fit: contain; object-position: center; padding: 12px; }
.appcard:hover .shot__img--contain img { transform: scale(1.03); }
/* Light sweeps across the capture on hover */
.shot__img::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,.13) 50%, transparent 62%);
  transform: translateX(-110%);
  pointer-events: none;
}
.appcard:hover .shot__img::after { animation: shine 1.1s var(--ease) forwards; }

.shot__img--empty {
  display: grid; place-items: center;
  cursor: default;
  background:
    repeating-linear-gradient(45deg, rgba(120,200,186,.10) 0 9px, transparent 9px 18px),
    var(--night-3);
}
.shot__ph {
  font-family: var(--font-mono);
  font-size: var(--label-sm); line-height: 1.8; letter-spacing: .06em;
  text-align: center;
  color: var(--fade-night); opacity: .55;
  padding: 10px;
}
.shot__zoom {
  position: absolute; left: 50%; bottom: 14px;
  transform: translateX(-50%) translateY(6px);
  padding: 7px 14px;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .08em;
  color: var(--on-night);
  background: rgba(3,13,18,.82);
  border: 1px solid var(--hair-night);
  border-radius: var(--radius-pill);
  opacity: 0;
  transition: opacity var(--t-fast), transform var(--t-fast) var(--ease);
}
.shot__img:hover .shot__zoom,
.shot__img:focus-visible .shot__zoom { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Card copy --------------------------------------------------------------- */
.appcard__body { flex: 1; padding: var(--pad-card-sm) var(--pad-card-sm) 4px; }
.appcard__head { display: grid; grid-template-columns: auto 1fr auto; gap: var(--gap-xs); align-items: start; }
.appcard__icon {
  width: 38px; height: 38px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--hair-night);
  border-radius: var(--radius-surface);
  color: var(--tealBright);
  background: rgba(47,191,163,.08);
  transition: background var(--t-base), transform var(--t-base) var(--ease);
}
.appcard__icon svg { width: 20px; height: 20px; }
.appcard:hover .appcard__icon { background: rgba(47,191,163,.18); transform: translateY(-2px); }
.appcard__title { font-size: 19px; font-weight: 800; letter-spacing: -.01em; margin: 0; }
.appcard__sub {
  font-size: 13px; font-weight: 600;
  color: var(--tealBright);
  margin: 3px 0 0;
}
.appcard__idx {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .1em;
  color: var(--fade-night); opacity: .38;
}
.appcard__text {
  font-size: 14.5px; line-height: 1.65;
  color: var(--fade-night); opacity: .92;
  margin: 16px 0 20px;
}
.appcard__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  margin: 0 22px; padding: 15px 0 20px;
  border-top: 1px solid var(--hair-night);
}
.appchip {
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: var(--track-sm); text-transform: uppercase;
  color: var(--fade-night); opacity: .7;
  border: 1px solid var(--hair-night);
  border-radius: var(--radius-pill);
  padding: 5px 11px;
}
.applink {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: .06em;
  color: var(--tealBright);
  text-decoration: none;
  background: none; border: 0; padding: 0;
  cursor: pointer;
}
.applink__arrow { transition: transform var(--t-fast) var(--ease); }
.applink:hover .applink__arrow { transform: translate(3px, -3px); }

/* -- Timeline: the era rail ------------------------------------------------ */
/* Every panel ships visible and stacked; enhance.js adds .is-tabbed and turns
   the row of decades into a real tablist. Without JavaScript the reader still
   gets all five eras in order. */
.eras { position: relative; margin-top: 46px; }
.eras__rail {
  position: relative;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
}
.eras__line {
  position: absolute; top: 33px; left: 10%; right: 10%; height: 2px;
  background: var(--hair-night);
  overflow: hidden;
}
.eras__fill {
  position: absolute; inset: 0 auto 0 0;
  width: 0;
  background: linear-gradient(90deg, var(--tealBright), var(--gold));
  transition: width 1.8s var(--ease);
}
.eras.is-in .eras__fill { width: 100%; }

.era {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 0 6px;
  background: none; border: 0;
  color: inherit;
  font-family: var(--font-sans);
  cursor: pointer;
  text-align: center;
}
.era__dot {
  position: relative;
  width: 68px; height: 68px;
  display: grid; place-items: center;
  border: 1px solid var(--hair-night);
  border-radius: 50%;
  background: var(--night-2);
  color: var(--fade-night);
  transition: transform var(--t-base) var(--ease), border-color var(--t-base),
              background var(--t-base), color var(--t-base), box-shadow var(--t-base);
}
.era__dot svg { width: 26px; height: 26px; }
.era:hover .era__dot { border-color: rgba(47,191,163,.55); color: var(--on-night); transform: translateY(-3px); }
.era.is-active .era__dot {
  border-color: var(--tealBright);
  background: rgba(47,191,163,.14);
  color: var(--tealBright);
  box-shadow: 0 0 0 7px rgba(47,191,163,.07);
  transform: scale(1.05);
}
.era__year {
  font-family: var(--font-mono);
  font-size: var(--label); letter-spacing: .14em;
  color: var(--fade-night); opacity: .7;
  transition: color var(--t-base), opacity var(--t-base);
}
.era.is-active .era__year { color: var(--tealBright); opacity: 1; }
.era__name {
  font-size: 13px; font-weight: 700; line-height: 1.45;
  max-width: 18ch;
  color: var(--fade-night); opacity: .78;
}
.era.is-active .era__name { color: var(--on-night); opacity: 1; }

.eras__panels { margin-top: 46px; }
.erapanel {
  display: grid; grid-template-columns: 160px 1fr;
  gap: 32px; align-items: baseline;
  padding: 28px 0;
  border-top: 1px solid var(--hair-night);
}
.eras.is-tabbed .erapanel { display: none; border-top: 0; padding: 0; }
.eras.is-tabbed .erapanel.is-active {
  display: grid;
  animation: fadeUp .45s var(--ease) both;
  border-top: 1px solid var(--hair-night);
  padding-top: 30px;
}
.erapanel__year {
  font-size: clamp(34px, 4.4vw, 54px);
  font-weight: 900; letter-spacing: -.03em; line-height: 1;
  color: var(--tealBright);
}
.erapanel__title { font-size: var(--step-2); font-weight: 800; letter-spacing: var(--tight-3); margin: 0; }
.erapanel__body { font-size: 16.5px; line-height: 1.7; color: var(--fade-night); opacity: .82; margin: 12px 0 0; max-width: 56ch; }

/* Section titles that centre on this page only */
.center-md { text-align: center; margin-left: auto; margin-right: auto; }
.eras-band .band-lede { margin-left: auto; margin-right: auto; text-align: center; }

/* -- Closing highlights ---------------------------------------------------- */
.hilites { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap-md) var(--gap); }
.hilite { display: grid; grid-template-columns: auto 1fr; gap: var(--gap-sm); align-items: start; }
.hilite__icon {
  width: 46px; height: 46px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  color: var(--accent);
  background: var(--paper);
  transition: background var(--t-base), color var(--t-base), transform var(--t-base) var(--ease);
}
.hilite__icon svg { width: 22px; height: 22px; }
.hilite:hover .hilite__icon { background: var(--accent); color: var(--paper2); transform: translateY(-3px); }
.hilite__title { font-size: 17px; font-weight: 800; letter-spacing: -.01em; margin: 4px 0 0; }
.hilite__body { font-size: 14.5px; line-height: 1.6; color: var(--sub); margin: 7px 0 0; }

/* -- Interaction layer ------------------------------------------------------
   Everything below is created or driven by enhance.js. None of it carries
   content, and none of it is required to read the page — each piece is either
   built in JS or stays invisible until JS starts feeding it a value.
   -------------------------------------------------------------------------- */

/* Hero light that follows the pointer. Sits over the grid, under the copy. */
.apphero__light {
  position: absolute; inset: 0;
  pointer-events: none;
  background: radial-gradient(680px circle at var(--mx, 30%) var(--my, 30%), rgba(47,191,163,.12), transparent 68%);
  opacity: 0;
  transition: opacity .5s var(--ease);
}
.apphero.is-lit .apphero__light { opacity: 1; }

/* Scroll parallax. The element only moves once JS writes --py to it. */
[data-parallax] { transform: translate3d(0, var(--py, 0px), 0); }

/* Era autoplay ring: a conic sweep masked down to a 2.5px band, so the active
   decade shows how long it has before the rail moves on. Injected by JS. */
.era__ring {
  position: absolute; inset: -6px;
  border-radius: 50%;
  background: conic-gradient(var(--tealBright) calc(var(--p, 0) * 1%), transparent 0);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2.5px));
          mask: radial-gradient(closest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2.5px));
  opacity: 0;
  transition: opacity var(--t-base);
  pointer-events: none;
}
.era.is-active .era__ring { opacity: .95; }
.eras.is-held .era__ring { opacity: .3; }

/* Keyboard hint on the filter bar */
.filterbar__hint {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono);
  font-size: var(--label-sm); letter-spacing: .06em;
  color: var(--fade-night); opacity: .5;
}
.kbd {
  display: inline-block; min-width: 17px; text-align: center;
  padding: 2px 5px;
  font-family: var(--font-mono); font-size: var(--label-sm); line-height: 1.2;
  color: var(--on-night);
  background: rgba(255,255,255,.08);
  border: 1px solid var(--hair-night);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
}

/* -- Reading progress ------------------------------------------------------ */
/* Injected by enhance.js, so it only ever exists where it can be driven. */
.readbar {
  position: fixed; inset: 0 0 auto 0;
  height: 3px; z-index: 200;
  transform: scaleX(var(--p, 0)); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--tealBright), var(--gold));
  pointer-events: none;
}


/* ============================================================================
   8  MOTION
   ============================================================================ */
@keyframes fadeUp { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
/* Case swap: rises and settles from slightly small, so a document reads as
   being placed on the table rather than cross-fading in place. */
@keyframes caseIn { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes riseIn { from { transform: translateY(115%); } to { transform: translateY(0); } }
@keyframes blink  { 0%,49% { opacity: 1; } 50%,100% { opacity: 0; } }

/* Applications page ambience */
@keyframes gridDrift { from { background-position: 0 0, 0 0; } to { background-position: 62px 62px, 62px 62px; } }
@keyframes floatGlow {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(-4%, 6%, 0) scale(1.09); }
}
@keyframes shine { to { transform: translateX(110%); } }
@keyframes pulseDot {
  0%, 100% { box-shadow: 0 0 0 0 rgba(47,191,163,.5); }
  60%      { box-shadow: 0 0 0 7px rgba(47,191,163,0); }
}

/* Outreach hero ambience. The two drifts are deliberately different lengths so
   the washes never return to the same arrangement twice while you read. */
@keyframes pulseGold {
  0%, 100% { box-shadow: 0 0 0 0 rgba(217,172,91,.55); }
  60%      { box-shadow: 0 0 0 8px rgba(217,172,91,0); }
}
@keyframes heroDriftA {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-6%, 5%, 0) scale(1.12); }
}
@keyframes heroDriftB {
  from { transform: translate3d(0, 0, 0) scale(1.06); }
  to   { transform: translate3d(7%, -6%, 0) scale(1); }
}
@keyframes drawRule { to { transform: scaleX(1); } }

.fade-up { animation: fadeUp .6s both; }
.cursor  { animation: blink 1.05s steps(1) infinite; color: var(--gold); }

.riseWrap {
  overflow: hidden;
  display: block;
  padding-bottom: .2em;
  margin-bottom: -.2em;
}
.riseWrap > span { display: block; animation: riseIn .85s var(--ease) both; }
.riseWrap:nth-of-type(1) > span { animation-delay: .05s; }
.riseWrap:nth-of-type(2) > span { animation-delay: .15s; }
.riseWrap:nth-of-type(3) > span { animation-delay: .23s; }

/* Scroll reveal — content is visible by default; JS opts in, so no-JS is safe */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
  will-change: opacity, transform;
}
.js-reveal [data-reveal].is-in { opacity: 1; transform: none; }

/* Card / tile hover lift */
.hover-lift { transition: transform var(--t-base) var(--ease), box-shadow var(--t-base); }
.hover-lift:hover { transform: translateY(-4px); box-shadow: var(--shadow-card); }

.media.has-img .hc-img { transition: transform .5s var(--ease); }
.media.has-img:hover .hc-img { transform: scale(1.03); }


/* ============================================================================
   9  RESPONSIVE — all breakpoints live here, keyed to classes only
   ============================================================================ */
@media (max-width: 1024px) {
  .slider-panel { grid-template-columns: 150px 1fr; gap: 28px; padding: 34px; }
  .grid--pillars { grid-template-columns: 1fr; }

  /* Applications: three cards across becomes two, and the terminal drops under
     the hero copy rather than being squeezed beside it. */
  .apphero__grid { grid-template-columns: 1fr; }
  /* One column again, so the headline gets the full hero scale back */
  .apphero .h-hero { font-size: var(--step-6); max-width: 20ch; }
  .appgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Wrapped doors are two rows, so the hero needs more than one screen: it
     stops being viewport-bound and sizes to its contents again. */
  .masthead { height: auto; max-height: none; }

  /* The split narrows before it breaks: the copy column gives up its share
     first, because a figure squeezed below about 40% stops reading as a figure. */
  .masthead__inner { grid-template-columns: minmax(0, 1fr) minmax(0, .8fr); gap: 28px; }
  .masthead__copy  { max-width: none; }

  /* Two rows of two. The bar stops being one object once it wraps, so it gives
     up its shared ground and the doors become four separate cards -- each
     keeping its own colour, lift and shadow. */
  /* Wrapped, the bar is no longer one object: it gives up its shared shadow and
     rounded ends, and each door becomes a card carrying its own colour. */
  .doors { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; overflow: visible; box-shadow: none; }
  .door + .door::before { display: none; }
  .masthead__doors { padding-bottom: var(--wrap-y); }
}

@media (max-width: 900px) {
  :root { --wrap-x: 22px; --wrap-y: 48px; --gap-lg: 34px; }

  .wrap--hero   { --wrap-y: 54px; padding-bottom: 44px; }
  .wrap--hero-l { --wrap-y: 56px; padding-bottom: 46px; }
  /* Every hero shortens by the same amount, including the one on .wr-wrap. */
  .hero-band { --hero-h: 420px; }
  .hero-band > .wrap    { --wrap-y: 40px; padding-bottom: 40px; }
  .hero-band > .wr-wrap { padding-top: 40px; padding-bottom: 40px; }
  .wrap--cta,
  .wrap--cta-l  { --wrap-y: 56px; }
  .wrap--strip  { --wrap-y: 26px; }
  .wrap--slim   { --wrap-y: 40px; }

  /* Every multi-column grid collapses from one place */
  .grid--2, .grid--3, .grid--split, .grid--aside, .grid--acc,
  .grid--media, .grid--video, .grid--join, .grid--form,
  .grid--endcap, .grid--label, .teaser, .hero__foot, .steps,
  .site-footer__grid {
    grid-template-columns: 1fr;
  }
  .grid--endcap, .teaser, .hero__foot { align-items: start; }
  .site-footer__grid { gap: 32px; }
  /* Stacked, the cell takes its natural place at the end of the column. */
  .site-footer__grid > :first-child,
  .site-footer__cta { grid-column: auto; grid-row: auto; }
  .site-footer__cta .btn { display: block; text-align: center; }

  /* Stacked, the teaser buttons are already the same width as the column, so the
     desktop min-width only risks overflowing a narrow phone. */
  .teaser .btn { min-width: 0; width: 100%; white-space: normal; }

  /* The split stops being a split. Photo first so the face is what opens the
     page on a phone, copy under it -- the reverse of the desktop reading order,
     which is why it is done with grid order rather than by moving the markup:
     the DOM keeps the copy first for a screen reader. */
  .masthead__inner {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    gap: 0;
  }
  .masthead__copy  { order: 2; padding: 30px 0 40px; max-width: none; }
  .masthead__lede  { max-width: none; font-size: 17px; }

  /* The figure keeps its own aspect -- it is a cut-out, so there is nothing to
     crop to and any fixed frame would just letterbox it. It is capped by height
     instead, so a tall portrait cannot push the copy off the first screen. */
  /* Stacked, the figure goes back into NORMAL FLOW. The desktop rules position
     it absolutely inside a viewport-bound row; here there is no such row, so
     those offsets have to be undone explicitly or the picture disappears. */
  .masthead__photo {
    order: 1;
    justify-self: center;
    align-self: auto;
    overflow: visible;
    margin-bottom: 0;
    padding-top: 12px;
    display: flex;
    justify-content: center;
  }
  .masthead__photo img {
    position: static;
    transform: none;
    margin: 0 auto;
    width: auto;
    height: auto;
    max-width: 78vw;
    max-height: 44vh;
  }

  /* The ghost is nowrap and far wider than a phone, so on a narrow screen it is
     what would push the page into horizontal scroll. It stays as texture but
     stops being able to widen the document. */
  .masthead__ghost { font-size: 26vw; opacity: .045; max-width: 100vw; overflow: hidden; }
  .masthead__sign  { font-size: 26px; }

  /* One door per row. Stacked, a lift upward would slide each card under the one
     above it, so the interaction becomes a nudge to the right instead. */
  .doors { grid-template-columns: 1fr; }
  .door  { padding: 22px 20px 24px; }
  .door:hover, .door:focus-visible { transform: translateX(6px); }

  .strip { grid-template-columns: repeat(2, 1fr); row-gap: 24px; }
  .strip__item { padding: 0 20px 0 0; }
  .strip__item:nth-child(2n) { padding-right: 0; border-right: 0; }

  .steps__item { padding: 0 0 24px; border-right: 0; border-bottom: 1px solid var(--line); }
  .steps__item:last-child { padding-bottom: 0; border-bottom: 0; }

  .slider-panel { grid-template-columns: 1fr; }
  .slider-panel__aside { border-right: 0; border-bottom: 1px solid var(--line); padding: 0 0 20px; }

  .numrow  { grid-template-columns: 56px 1fr; gap: 18px; }
  .numrow > .meta { grid-column: 2; }

  .event-row { grid-template-columns: 90px 1fr auto; gap: 18px; padding: 20px; }

  .pillar-panel { padding: 28px; min-height: 0; }
  .prose { font-size: 17px; }
  .eyebrow--vert { writing-mode: horizontal-tb; transform: none; }
  .callout { padding-left: 20px; }

  /* Mobile nav ------------------------------------------------------------
     The drawer was inheriting the desktop row wholesale: 13.5px labels, a 4px
     gap and 3px of padding, which on a phone gave six tap targets about 20px
     high stacked almost on top of each other. Opened as a full-width panel the
     links are the only thing in it, so they are sized as destinations rather
     than as a compact header row -- 17px, each on its own line with a rule
     under it, and a row box tall enough to hit without aiming. */
  .nav__links { display: none; }
  .nav-toggle { display: block; }
  body.nav-open .nav__links {
    display: flex;
    position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--paper);
    border-bottom: 2px solid var(--ink);
    box-shadow: 0 18px 34px rgba(18,55,66,.14);
    padding: 8px var(--wrap-x) 20px;
    z-index: 90;
  }
  body.nav-open .nav__link {
    font-size: 17px;
    padding: 15px 0;
    border-bottom: 1px solid var(--line);
    border-top: 0;
  }
  /* The underline that marks the current page reads as a border on the row
     itself here, so it moves to a left bar and the row keeps its own rule. */
  body.nav-open .nav__link[aria-current="page"] {
    color: var(--blue);
    box-shadow: inset 3px 0 0 var(--blue);
    padding-left: 12px;
  }
  body.nav-open .nav__link:hover { border-bottom-color: var(--line); }
  /* The rule under the last destination would otherwise float above the CTA's
     own gap, reading as a line belonging to nothing. */
  body.nav-open .nav__link:last-of-type { border-bottom: 0; }
  /* The CTA is an action, not a sixth destination -- it sits clear of the list. */
  body.nav-open .nav__links .btn {
    margin-top: 16px;
    text-align: center;
    padding: 15px 24px;
    font-size: 15px;
  }

  /* The burger itself was a 24px mark in an 8px pad: a 40px box, and visually
     small against a 21px wordmark. */
  .nav-toggle { padding: 10px; margin-right: -10px; }
  .nav-toggle span { width: 26px; margin: 6px 0; }
  .topbar__mid { display: none; }
  .topbar { grid-template-columns: 1fr auto; }

  .blog-card--scroll { flex-basis: 78vw; }
  .media--cap { max-width: none; }

  .factgrid { grid-template-columns: repeat(2, 1fr); gap: 28px; }

  /* Stack the aim cards — three across is unreadable at this width. There is no
     hover here, so the photo header is the tap target and each card opens on
     tap; the statement stays visible either way. */
  .aimset { grid-template-columns: 1fr; gap: 16px; }
  .aim__photo { height: 200px; }

  /* Consulting: the practitioner card drops under the headline, the document
     stage moves above the copy, and the sticky bar drops to just its link row
     so it stays one line high. */
  /* Stacked, the practitioner card leads: it is the first thing an advisory
     visitor looks for, and it is what the headline above it is about. Done with
     grid order rather than by moving the markup, so the DOM keeps the headline
     first for a screen reader. */
  .hero-consult { grid-template-columns: 1fr; }
  .hero-consult > :first-child { order: 2; }
  .who { order: 1; max-width: 360px; margin: 0 auto; }
  .cases { grid-template-columns: 1fr; }
  .cases__stage { max-width: 420px; }
  .pagenav__inner { min-height: 52px; gap: 0; padding: 0 12px; }
  .pagenav__label,
  .pagenav__cta { display: none; }
  [data-page="consulting"] .band[id] { scroll-margin-top: 58px; }

  /* Applications ---------------------------------------------------------- */
  .appgrid, .hilites { grid-template-columns: 1fr; }
  .hstats { gap: 28px; }
  .terminal__body { min-height: 0; font-size: 12px; padding: 18px 16px 22px; }

  /* The era rail turns into a scrollable row of decades: five 68px nodes will
     not fit across a phone, and stacking them would bury the panel. */
  .eras__rail {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 116px;
    overflow-x: auto;
    scrollbar-width: none;
    /* The active node lifts and scales, and its ring is a 7px box-shadow, so
       the row needs headroom of its own -- with the default padding the top of
       the selected circle and the whole upper arc of its ring were being cut
       off by the scroll container's own edge. */
    padding: 10px 0 6px;
  }
  .eras__rail::-webkit-scrollbar { display: none; }
  /* Without a connecting line the decades read as five loose buttons rather than
     one timeline. The element itself is dropped -- absolutely positioned inside
     a scroll container it only ever spans one viewport, not the scrolled row --
     and the line is painted on the rail instead, as a background that is by
     definition the full scroll width. It sits level with the dot centres. */
  .eras__line { display: none; }
  .eras__rail {
    background-image: linear-gradient(var(--hair-night), var(--hair-night));
    background-repeat: no-repeat;
    background-position: 0 44px;
    background-size: 100% 2px;
  }
  .era__name { font-size: 12px; }
  .eras__panels { margin-top: 32px; }
  .erapanel { grid-template-columns: 1fr; gap: 12px; }
  .center-md, .eras-band .band-lede { text-align: left; }
  .glow { filter: blur(6px); opacity: .38; }
}

@media (max-width: 520px) {
  .strip { grid-template-columns: 1fr; }
  .strip__item { padding: 0; border-right: 0; }
  .grid--4 { grid-template-columns: 1fr; }
  .rowlist__row { flex-direction: column; align-items: flex-start; gap: 4px; }
  .event-row { grid-template-columns: 70px 1fr; }
  .event-row__arrow { display: none; }
  .btn { padding: 14px 24px; }
  .factgrid { grid-template-columns: 1fr; }
  .thumb { width: 84px; height: 63px; }

  /* Term over description — 108px of label column is too much of a 320px screen */
  .glance__row { grid-template-columns: 1fr; gap: 3px; }
  .case-tab { flex: 1; }

  /* Applications: the three counters stack. Two-up left the third figure alone
     on a second row under a gap, which read as a layout fault rather than a
     third statistic -- one per line is even, and each figure keeps its label
     directly under it. */
  .hstats, .hstats--strip { grid-template-columns: 1fr; gap: 22px; }
  /* dt/dd already carry their own 10px, so the cell needs no gap of its own. */
  .hstat__label { margin-top: 8px; }
  .filterbar__count { margin-left: 0; }
  .appcard__foot { padding-bottom: 18px; }
}

@media (min-width: 521px) and (max-width: 900px) {
  .grid--4 { grid-template-columns: repeat(2, 1fr); }
}

/* Touch devices never fire :hover, so the zoom affordance is shown outright */
@media (hover: none) {
  .artifact__zoom { opacity: 1; transform: translateX(-50%); }
  .shot__zoom { opacity: 1; transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .js-reveal [data-reveal] { opacity: 1 !important; transform: none !important; }
  .hover-lift:hover { transform: none; }
  .media.has-img:hover .hc-img { transform: none; }
  .aim:hover { transform: none; }
  .aim:hover .aim__photo { transform: none; }
  .aim:hover .aim__badge { transform: none; }
  .artifact__frame:hover img { transform: none; }
  .artifact__frame:hover { transform: none; }
  .steps--flow .steps__item:hover { transform: none; }
  .ecard:hover .ecard__icon { transform: none; }
  .case-tab:hover { transform: none; }

  /* Outreach: the hero keeps its aura, loses the drift, and the rule under the
     highlighted words is simply already drawn */
  .hero-out .hero__badge::before { animation: none; }
  .hero-out .hl::after { animation: none; transform: scaleX(1); }
  .hero-out .btn:hover { transform: none; }

  /* Applications: the page keeps its look, loses its movement */
  .codegrid, .glow, .hero__badge--live::before { animation: none; }
  [data-parallax] { transform: none; }
  .appcard { transform: none; }
  .appcard:hover { transform: none; }
  .appcard:hover .shot__img img { transform: none; }
  .appcard:hover .shot__img::after { animation: none; }
  .appcard:hover .appcard__icon { transform: none; }
  .era:hover .era__dot { transform: none; }
  .era.is-active .era__dot { transform: none; }
  .eras__fill { transition: none; }
  .hilite:hover .hilite__icon { transform: none; }
  .btn--teal:hover { transform: none; }
  .terminal.is-typing .terminal__line { visibility: visible; }
}


/* ============================================================================
   WRITING — the paper page
   ----------------------------------------------------------------------------
   The author page runs on its own palette: light olive paper, ink black, one
   deep-olive accent. Profession signals colour on this site — software gets
   night, the Foundation gets green, publishing gets paper stock.

   Two rules keep it part of the same site rather than a separate one:
     · the serif (Cormorant Garamond) is the ONLY new face — body copy stays
       Hanken Grotesk and every uppercase label stays IBM Plex Mono;
     · the olive tokens are namespaced --w-* and set on the body, so the shared
       header and footer keep the site palette and nothing else moves.

   Everything below is scoped to [data-page="writing"] or to a .wr- class this
   page alone uses.
   ============================================================================ */

/* -- Page shape languages --------------------------------------------------
   Each page restates the three shape roles once. Everything cornered on that
   page follows, and the difference between pages stays deliberate rather than
   accumulating component by component.

   Applications sits at the same surface radius as the shared pages: it reads as
   the same family of surfaces, and the page already carries its identity in the
   night palette and the tilt. -------------------------------------------- */
body[data-page="application"] {
  --radius-surface: var(--r-md);
  --radius-control: var(--r-xs);
  --radius-pill:    var(--r-pill);
}

/* The two editorial pages are square. Rules and bands do the separating there,
   so a corner radius would be the only soft thing on the page. */
body[data-page="writing"],
body[data-page="about"],
body[data-page="leasted-1p"] {
  --radius-surface: 0;
  --radius-control: 0;
  --radius-pill:    0;
}

body[data-page="writing"] {
  /* -- Surfaces --------------------------------------------------------- */
  /* The paper darkened one step: what was the deeper band is now the ground,
     and the deeper band goes darker still. Both text colours below moved with
     it -- the old --w-sub and --w-olive-deep measured 3.8:1 and 4.3:1 on the
     new deeper ground, so they had to come down or the copy on it would have
     failed AA. */
  --w-cream:      #E5E8D8;   /* page ground, hero, alternating bands        */
  --w-cream2:     #C7CBA8;   /* the deeper band                            */
  --w-white:      #FBFCF7;   /* cards, article grid, cover pads            */
  --w-ink:        #1A1C14;   /* all display + body text — 10.3:1 on cream2 */
  --w-sub:        #4C5140;   /* secondary copy, labels, meta — 4.9:1 worst */
  /* The old hairline was a step lighter than the old ground. Against the
     darkened paper it measured 1.15:1 and effectively disappeared, so it comes
     down too. Separators are decorative rather than meaning-bearing, so the
     bar is legibility, not the 3:1 non-text minimum. */
  --w-line:       #B4BA9C;   /* hairlines, card borders, grid gutters      */

  /* -- The one accent, in three jobs ------------------------------------- */
  /* --w-olive is a FILL only (2.2:1). Anything olive that carries meaning as
     text — a word, a tag, the active dot — uses --w-olive-deep. */
  --w-olive:      #C8CE72;
  --w-olive-mid:  #A9B25A;   /* short decorative rules, never behind text  */
  --w-olive-deep: #565211;   /* olive as text: 4.8:1 worst (the deep band) */

  --w-serif:  'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --w-script: 'Parisienne', 'Snell Roundhand', cursive;

  /* The card surface for this page. .wr-book is .card with these applied. */
  --card-bg:     var(--w-white);
  --card-line:   var(--w-line);
  --card-hover:  var(--w-olive-mid);
}

/* ---------------------------------------------------------------------------
   WRITING — the page colour
   #C7CBA8 is the Writing page's own ground, and the hero and the closing press
   band both take it. It is a LIGHT ground, unlike the bronze it replaces, so
   every token the band hands down has to flip with it: cream type on this reads
   1.44:1 and white 1.68:1, against 10.3:1 for ink. The band therefore carries
   ink type, an ink hairline, and a bronze accent (4.1:1) for the eyebrow.
   --------------------------------------------------------------------------- */
body[data-page="writing"] .band--bronze {
  --band-bg: var(--olive);        /* #C7CBA8, already a site token */
  --band-fg: var(--ink);
  --accent: var(--bronze);
  --band-eyebrow: var(--bronze);
  --band-hair: rgba(26, 28, 20, .22);
}
/* The highlighted words in the hero headline default to the gold that worked on
   the dark bronze; on this ground that is 1.9:1, so they take bronze instead. */
body[data-page="writing"] .band--bronze .hl { color: var(--bronze); }
/* .eyebrow--hero hardcodes --gold-text, which is mixed for a dark ground and
   all but vanishes on this one (1.9:1). Bronze itself only reaches 4.07:1 here
   and this is small uppercase text, so it takes a step darker: 4.85:1. The
   headline can keep plain --bronze because large text needs only 3:1. */
body[data-page="writing"] .band--bronze .eyebrow--hero { color: #6B4829; }
/* Same reason for the lede, which runs at a muted tint of the band foreground. */
body[data-page="writing"] .band--bronze .hero-lede,
body[data-page="writing"] .band--bronze .cta-lede { color: #3A3B2C; }

body[data-page="writing"] ::selection { background: #EBDF9E; color: var(--w-ink); }

/* The ring colour for this page. The rule itself lives once, in the base. */
body[data-page="writing"] .wr-band { --focus: var(--w-olive-deep); }

/* -- Bands and the measure ------------------------------------------------- */
/* Section rhythm alternates cream → white → cream2 → white → cream. Two
   identical grounds never sit next to each other, so no rule is needed to
   separate them. */
/* Spacing comes from the site rhythm (--wrap-y), not a number of this page's
   own. The Writing page had hard-coded 76px here, 92px on its CTA and 58px in
   its own media query -- so scrolling from Consulting into Writing changed the
   gap between sections for no reason the reader could name. Binding to the
   token also means the 900px breakpoint shortens this page with every other. */
.wr-band {
  background: var(--w-cream);
  color: var(--w-ink);
  padding: var(--wrap-y) 0;
}
/* The bands all sit on the hero's paper now; a hairline keeps the seam between
   two same-coloured sections legible without a colour change. --w-white stays
   in play for the cards that sit ON the paper. */
.wr-band--white  { background: var(--w-cream); border-top: 1px solid var(--w-line); }
/* The quote section is short by design -- three sentences and a row of dots --
   so it takes two thirds of the band's usual air rather than the full wrap that
   suits a section with real content in it. */
.wr-band--cream2 { background: var(--w-cream2); padding: calc(var(--wrap-y) * .5) 0; }
.wr-band--flush  { padding: 0; }

.wr-wrap { max-width: var(--wrap-max); margin: 0 auto; padding: 0 var(--wrap-x); }
.wr-wrap--slim { max-width: 900px; }

/* -- Type ------------------------------------------------------------------ */
/* font-variant-caps does the large-cap / small-cap setting that gives the page
   its bookish voice — the characters stay lowercase in the markup, so the text
   is still read as a sentence by a screen reader. */
.wr-h {
  font-family: var(--w-serif);
  font-weight: 400;
  font-variant-caps: small-caps;
  /* Serif small-caps sit tighter than the sans at the same step, so this is the
     one place the scale's tracking is overridden rather than inherited -- the
     face needs it, and the step is still the shared one. */
  letter-spacing: .01em;
  line-height: 1.16;
  font-size: var(--step-4);
  margin: 6px 0 0;
  color: var(--w-ink);
}
.wr-h--sm { font-size: var(--step-3); }
/* font-variant-caps shrinks lowercase but leaves real capitals full height, so
   a heading containing an acronym renders it towering over its own line and
   reads as a second font dropped into the middle of the sentence. Headings that
   carry one opt out. */
.wr-h--plain { font-variant-caps: normal; }

/* The margin reset is not cosmetic. This label is a <p> on every one of its six
   sites, so without it the browser's default 1em top margin pushes the whole
   left column down 19px while the column beside it starts flush at the top --
   which read as a section whose content was not centred in its own band, even
   though the band's 76px padding was correct on both sides. The shared
   .eyebrow never showed this because it is always a <div>. */
.wr-eyebrow {
  font-family: var(--w-serif);
  font-style: italic;
  font-size: 19px;
  font-weight: 400;
  color: var(--w-sub);
  margin: 0;
}

.wr-label {
  font-family: var(--font-mono);
  font-size: var(--label);
  font-weight: 400;
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--w-sub);
}
.wr-label--olive { color: var(--w-olive-deep); }

.wr-copy {
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.78;
  color: var(--w-ink);
  max-width: 52ch;
  margin: 16px 0 0;
}
.wr-copy--sub { color: var(--w-sub); }

.wr-rule {
  width: 54px; height: 2px; border: 0;
  background: var(--w-olive-mid);
  margin: 22px 0 0;
}

.wr-btn {
  display: inline-block;
  font-family: var(--font-sans);
  /* Matches .btn. The square corner is this page's signal; a smaller button was
     drift, not a decision -- the same action should be the same size sitewide. */
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .04em;
  line-height: 1.2;
  padding: 16px 32px;
  background: var(--w-olive);
  color: var(--w-ink);             /* ink on olive is 10.4:1 — never white */
  border: 1px solid var(--w-olive);
  border-radius: var(--radius-control);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.wr-btn:hover { background: var(--w-olive-mid); border-color: var(--w-olive-mid); }

.wr-sechead {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 40px;
  align-items: end;
  padding-bottom: 26px;
  border-bottom: 1px solid var(--w-line);
}
.wr-sechead__aside { text-align: right; max-width: 42ch; }
.wr-sechead__aside .wr-copy { margin: 0 0 18px auto; }
.wr-sechead--center { display: block; text-align: center; border: 0; padding-bottom: 0; }
.wr-sechead--center .wr-h { margin-top: 4px; }

/* ---------------------------------------------------------------------------
   1  HERO — new release
   The headline runs the full width; the three columns below hang off it, and
   the cover rides up over the headline baseline so the two read as one block.
   --------------------------------------------------------------------------- */
/* The shared .hero-band pads the wrap to the common height, so this section
   adds none of its own -- it would stack on top and make the banner taller
   than the other three. */
.wr-hero { padding: 0; }
.wr-hero__kicker { display: block; text-align: center; }
.wr-hero__title {
  font-family: var(--w-serif);
  font-weight: 400;
  font-variant-caps: small-caps;
  font-size: clamp(56px, 8vw, 116px);
  line-height: .95;
  letter-spacing: .005em;
  text-align: center;
  /* The jacket used to tuck up past this baseline by -.22em. That was tuned
     against a 300px cover; at the narrower size it stopped reading as a tuck
     and started cutting into the letterforms, so the row now sits below the
     headline instead of climbing into it. */
  margin: 18px 0 34px;
  color: var(--w-ink);
}
/* Copy, jacket, copy. The two text columns align to the TOP of the row rather
   than hanging from its foot: the claim on the left and the signature on the
   right then start on the same line, and the three columns read as one row
   instead of three blocks resting on a shelf at different heights.

   The jacket is 244px -- wide enough to read as a book, narrow enough that the
   banner still clears the height the other three pages share. */
.wr-hero__row {
  display: grid;
  grid-template-columns: 1fr 244px 1fr;
  gap: 56px;
  align-items: start;
}
/* Both text columns hang from the bottom line, so the claim on the left and the
   copy on the right finish on the jacket's baseline. Only the jacket itself
   starts at the top, because the headline sits directly over it. */
.wr-hero__row > .wr-rise--1,
.wr-hero__row > .wr-hero__aside { align-self: end; }
/* This is the one banner that holds a real object at real size. A 2:3 jacket
   small enough to fit the shared 570px would be ~193px wide -- too small to
   read as a book, which is the whole point of the section. So this page states
   a taller banner rather than shrinking its subject to fit one. It is the only
   --hero-h override on the site, and it is deliberate. */
body[data-page="writing"] .hero-band { --hero-h: 648px; }
.wr-hero__claim {
  font-family: var(--w-serif);
  font-weight: 500;
  font-size: 38px;
  line-height: 1.18;
  margin: 0;
}
.wr-hero__claim em { font-style: normal; color: var(--w-olive-deep); }

/* height:auto is load-bearing: the img carries width/height attributes for
   layout stability, and those map to CSS presentational hints — without this
   the intrinsic 1271px height wins and the jacket is cropped to a stripe. The
   jacket is shown whole rather than cropped to a nominal 2:3. */
.wr-hero__cover img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--w-white);
  box-shadow: 0 26px 60px rgba(22,19,15,.18);
}
.wr-hero__sig {
  font-family: var(--w-script);
  font-size: 34px;
  line-height: 1;
  color: var(--w-ink);
  margin: 0 0 14px;
}
.wr-hero__aside .wr-copy { margin-top: 0; max-width: 34ch; }
.wr-hero__aside .wr-btn { margin-top: 22px; }

/* The hero settles rather than arrives: a short fade up, staggered down the
   three columns. */
.wr-rise { animation: wrFadeUp .9s var(--ease) both; }
.wr-rise--1 { animation-delay: .05s; }
.wr-rise--2 { animation-delay: .15s; }
.wr-rise--3 { animation-delay: .25s; }
@keyframes wrFadeUp {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------------------
   2  LIBRARY
   --------------------------------------------------------------------------- */
.wr-books {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 26px;
  margin-top: 42px;
}
/* A book jacket is a card. It carries only what a book needs that a card does
   not: the taller foot that gives the jacket room to sit. Ground, border,
   radius and hover all come from the page's card variables above. */
.wr-book { --card-pad: 30px 30px 34px; }
/* The two jackets are different shapes — one a 2:3 front cover, one a full
   wrap-around spread — so they are contained rather than cropped and the cream
   pad absorbs whatever letterboxing is left. */
.wr-book__pad {
  background: var(--w-cream);
  display: flex; align-items: center; justify-content: center;
  padding: 34px;
  height: 400px;                   /* fixed, so both jackets sit on one line */
}
.wr-book__pad img {
  display: block;
  max-width: 100%; max-height: 100%; width: auto;
  box-shadow: 0 10px 26px rgba(22,19,15,.14);
}
/* Two lines reserved: one title wraps and one does not, and without this the
   category and blurb under them would sit at two different heights. */
.wr-book__title {
  font-family: var(--w-serif);
  font-weight: 600;
  font-size: 26px;
  line-height: 1.2;
  text-align: center;
  margin: 24px 0 0;
  min-height: 2.4em;
}
.wr-book__cat { display: block; text-align: center; margin-top: 12px; }
.wr-book__note { text-align: center; margin: 12px auto 0; max-width: 44ch; }

/* Body copy never drops below 16px on this page, so a centred note centres by
   margin rather than by shrinking. */
.wr-copy--center { margin-inline: auto; text-align: center; }

/* ---------------------------------------------------------------------------
   3  ABOUT THE WRITER — copy left, full-bleed portrait right
   --------------------------------------------------------------------------- */
/* An even 50/50. Photo left, copy right — the copy's outer padding is the one
   that has to reach the page gutter. The portrait is a tight headshot, so it is
   framed off its upper third rather than dead centre. */
.wr-about { display: grid; grid-template-columns: 1fr 1fr; align-items: stretch; }
.wr-about__copy {
  background: var(--w-cream2);
  padding: 76px max(var(--wrap-x), calc((100vw - var(--wrap-max)) / 2 + var(--wrap-x))) 76px 60px;
  display: flex; flex-direction: column; justify-content: center;
}
/* A stray quote in front of this rule made the whole declaration invalid, which
   is why the button under the About copy had no spacing of its own while the
   matching one in a split block did. */
.wr-about__copy .wr-btn { margin-top: 28px; align-self: flex-start; }
/* The button in a split block had no spacing of its own, so it inherited the
   16px gap that separates one paragraph from the next -- far too tight for the
   end of a block, and visibly unequal against the space above the heading. */
.wr-split .wr-btn { margin-top: 28px; }
.wr-about__photo { min-height: 520px; }
.wr-about__photo img {
  display: block; width: 100%; height: 100%; min-height: 520px;
  object-fit: cover; object-position: 50% 24%;
}

/* ---------------------------------------------------------------------------
   4  SELECTED ARTICLES
   A 1px gap over a --line ground: the cells read as one ruled table rather
   than six floating boxes.

   Type in this section is deliberately two faces, not three: Cormorant for the
   headline, Hanken for everything else. The mono that labels the rest of the
   site was a third voice inside a 300px cell and made the cards look like
   spec sheets rather than an index of articles.
   --------------------------------------------------------------------------- */
/* Category and date are both selects and both sit on the same row, at the left.
   Category was a wrapping row of chips, which on a phone grew to three lines of
   pills above a single date control and made the two filters look like different
   kinds of thing. They narrow the same list, so they read as one pair. */
.wr-filters {
  display: flex; flex-wrap: wrap; align-items: end;
  gap: 14px 22px;
  margin-top: 38px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--w-line);
}
.wr-filters__field { display: flex; align-items: center; gap: 10px; }
.wr-filters__label {
  font-family: var(--font-sans); font-size: 13px; color: var(--w-sub);
  white-space: nowrap;
}
/* The native control drew the OS arrow, which is a different mark on every
   platform and matched nothing else on the site. Drawing the chevron as a
   background keeps the same glyph the disclosures use, at the same angle.
   The SVG is inline rather than a file so it inherits nothing and cannot 404. */
.wr-select {
  font-family: var(--font-sans); font-size: 13px; color: var(--w-ink);
  background: var(--w-white);
  border: 1px solid var(--w-line); border-radius: var(--radius-control);
  padding: 11px 34px 11px 12px; cursor: pointer;

  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23565211' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px 6px;
}
/* Edge/IE draw their own arrow on top of the one above. */
.wr-select::-ms-expand { display: none; }
.wr-select:focus { border-color: var(--w-olive-deep); }
/* Both controls take the same width so the pair reads as one row of filters
   rather than two unrelated boxes sized by their longest option. */
.wr-filters .wr-select { min-width: 150px; }

.wr-artgrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--w-line);
  border: 1px solid var(--w-line);
  margin-top: 30px;
}
.wr-art {
  display: flex; flex-direction: column;
  background: var(--w-white);
  padding: 0 0 26px;
  min-height: 176px;
  text-decoration: none;
  color: inherit;
  transition: background 200ms var(--ease);
}
a.wr-art:hover { background: #F6F8EE; }
.wr-art.is-out { display: none; }         /* filtered away */
.wr-art.is-over { display: none; }        /* beyond the current page */

/* The image is optional — a post without one simply starts at its category. */
/* Every card carries the same 16:10 block, image or not. A post without one
   gets a laid-paper panel — deliberately a surface, so the row stays even and
   the cell never reads as a picture that failed to load. */
.wr-art__img { display: block; overflow: hidden; aspect-ratio: 16 / 10; }
.wr-art__img img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
}
.wr-art__img--blank {
  background:
    repeating-linear-gradient(135deg,
      rgba(157,164,133,.28) 0 1px, transparent 1px 14px),
    var(--w-cream);
}
/* The text block sits at the foot of the cell. Posts with an image and posts
   without sit in the same row, and anchoring the text is what keeps their tags
   and titles on one line across that row instead of starting wherever each
   cell's image happens to end. */
.wr-art__body { display: block; margin-top: auto; padding: var(--pad-card) var(--pad-card) 0; }
.wr-art__tag {
  display: block;
  font-family: var(--font-sans);
  font-size: 12px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--w-olive-deep);
}
.wr-art__title {
  font-family: var(--w-serif);
  font-weight: 500;
  font-size: 24px;
  line-height: 1.22;
  margin: 14px 0 0;
}
.wr-art__meta {
  display: block;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--w-sub);
  margin-top: 18px;
}

.wr-artgrid__empty { margin-top: 34px; }
.wr-more { display: flex; justify-content: center; margin-top: 34px; }

/* ---------------------------------------------------------------------------
   5  BYLINE HISTORY
   --------------------------------------------------------------------------- */
.wr-byline { display: grid; grid-template-columns: .75fr 1.25fr; gap: 60px; }
.wr-rows { align-self: start; }
.wr-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 24px;
  border-top: 1px solid var(--w-line);
  padding: 20px 0;
}
.wr-row:last-child { border-bottom: 1px solid var(--w-line); }
.wr-row__name { font-family: var(--w-serif); font-size: 28px; font-weight: 400; line-height: 1.2; }

/* ---------------------------------------------------------------------------
   6  PRAISE — rotating pull quote
   The quotes are stacked in one grid cell so the band is as tall as the longest
   of them and nothing jumps when the rotation advances.
   --------------------------------------------------------------------------- */
/* All three quotes share one grid cell so they can cross-fade in place. That
   also means the block is as tall as the LONGEST of them -- 551 characters here
   against a shortest of 226 -- so a short quote used to sit in a mostly empty
   band. A floor on the cell keeps the height steady without letting the longest
   quote set it for the other two. */
/* The three quotes share one cell so they can cross-fade, which means the block
   is as tall as the LONGEST -- 551 characters against a shortest of 226. That
   is left alone deliberately: capping it would clip a testimonial mid-sentence,
   which is worse than a band that breathes. The height is managed by making the
   type smaller and the measure wider instead, so the long quote needs fewer
   lines to begin with. */
.wr-quotes { display: grid; margin-top: 22px; align-items: center; }
.wr-quote {
  grid-area: 1 / 1;
  align-self: center;   /* the stack is as tall as the longest quote — a short
                           one sits centred in it rather than leaving a hole */
  margin: 0;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-slow) var(--ease);
}
.wr-quote.is-active { opacity: 1; visibility: visible; }
.wr-quote__text {
  font-family: var(--w-serif);
  font-style: italic;
  font-weight: 400;
  /* A step down. At 22-30px these ran larger than the pull quote elsewhere on
     the page, which made a three-line testimonial the loudest thing in the
     section. The measure is capped too: 30px italic serif across the full slim
     wrap was well past a comfortable line length. */
  font-size: clamp(16px, 1.35vw, 18px);
  line-height: 1.7;
  margin: 0 auto;
  max-width: 66ch;
}
.wr-quote__by { display: block; margin-top: 18px; }
.wr-dots { display: flex; justify-content: center; gap: 2px; margin-top: 22px; }
.wr-dot {
  width: 44px; height: 44px;        /* the target is 44px; the mark inside is 9px */
  padding: 0; border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.wr-dot::before {
  content: ''; width: var(--dot); height: var(--dot); border-radius: var(--r-pill);
  background: var(--muted-2);
  transition: width var(--t-base) var(--ease), background var(--t-fast) var(--ease);
}
.wr-dot[aria-pressed="true"]::before { width: var(--dot-active); background: var(--w-olive-deep); }

/* ---------------------------------------------------------------------------
   7  IN DEVELOPMENT / 8  THE LIST — 50 / 50 splits
   --------------------------------------------------------------------------- */
.wr-split { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; }
.wr-split__media { border: 1px solid var(--w-line); }
.wr-split__media img { display: block; width: 100%; height: auto; }
.wr-split__media--crop img { aspect-ratio: 4 / 3; object-fit: cover; }
.wr-split__media--tall img { aspect-ratio: 5 / 4; object-fit: cover; object-position: 50% 18%; }

.wr-note { text-align: center; }
.wr-note .wr-h { margin-bottom: 4px; }
.wr-note .wr-form { margin-inline: auto; }
.wr-form { display: flex; gap: 10px; margin-top: 26px; max-width: 460px; }
.wr-input {
  flex: 1;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--w-ink);
  background: var(--w-white);
  border: 1px solid var(--w-line);
  border-radius: var(--radius-control);
  padding: 14px 16px;
}
.wr-input::placeholder { color: var(--w-sub); opacity: 1; }
.wr-input:focus { border-color: var(--w-olive-deep); }
.wr-form__status {
  font-family: var(--font-sans); font-size: 14px;
  margin-top: 14px; min-height: 1.4em; color: var(--w-olive-deep);
}
.wr-form__status.is-err { color: #7A2E12; }
.wr-form__status a { color: inherit; }

/* ---------------------------------------------------------------------------
   9  WHERE TO FIND THE WORK
   The only iconography on the page: 22px monoline strokes in generic shapes —
   a storefront, a book, a screen, an envelope. Never a company mark.
   --------------------------------------------------------------------------- */
.wr-finds {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 8px 52px;
  margin-top: 38px;
}
.wr-find {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0;                  /* 22px icon + 22px padding = 44px target */
  color: var(--w-sub);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}
.wr-find:hover { color: var(--w-olive-deep); }
.wr-find svg { width: 22px; height: 22px; flex: none; }
.wr-find svg[fill="currentColor"] { width: 20px; height: 20px; }
.wr-find__name { font-family: var(--w-serif); font-size: 24px; line-height: 1; }

/* ---------------------------------------------------------------------------
   9  THE CLOSING CTA
   The applications page ends on .appcta — a centred headline, one lede and two
   actions on the night surface. This is the same block for a different trade,
   so it keeps the structure and drops the surface: it sits on the page's own
   paper, which is what the rest of the page is made of, and separates itself
   with a rule and its own vertical air rather than a colour change.
   --------------------------------------------------------------------------- */
/* The CTA's secondary action jumps back up to the library, which the mobile
   header overlays — so the target keeps a header's worth of margin above it.
   #articles needs the same for the same reason, and is now also an INCOMING
   target: the Outreach page's 07 band links straight to it. */
#library,
#articles { scroll-margin-top: 80px; }

/* The closing CTA is the one band that runs taller than the rest, matching
   .wrap--cta-l on the shared pages rather than inventing its own pair. */
.wr-cta {
  border-top: 1px solid var(--w-line);
  padding: calc(var(--wrap-y) + 20px) 0;
  text-align: center;
}
.wr-cta__h { font-size: var(--step-5); margin: 0; }
.wr-cta__lede {
  margin: 20px auto 0;
  max-width: 58ch;
  color: var(--w-sub);
}
.wr-cta__actions {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 14px;
  margin-top: 34px;
}

/* The secondary action is the same button with the fill taken out, so the two
   read as one pair with a clear first choice. */
.wr-btn--ghost {
  background: none;
  border-color: var(--w-olive-deep);
  color: var(--w-olive-deep);
}
.wr-btn--ghost:hover { background: var(--w-cream2); border-color: var(--w-olive-deep); }

/* The retailer row that used to be its own section, folded in under a rule. */
.wr-cta__finds { margin-top: 56px; padding-top: 30px; border-top: 1px solid var(--w-line); }
.wr-cta__findlabel {
  display: block;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--w-sub);
}
.wr-cta__finds .wr-finds { margin-top: 14px; gap: 4px 44px; }
.wr-cta__finds .wr-find__name { font-size: 21px; }

/* ---------------------------------------------------------------------------
   Responsive
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .wr-artgrid { grid-template-columns: repeat(2, 1fr); }
  .wr-hero__row { grid-template-columns: 1fr 260px 1fr; gap: 34px; }
  .wr-hero__claim { font-size: 30px; }
}

@media (max-width: 860px) {
  /* The cover leads on a phone — it is the release the section is about — and
     stops overlapping, since there is no headline column beside it. */
  .wr-hero__row { grid-template-columns: 1fr; gap: 34px; justify-items: center; text-align: center; }
  .wr-hero__title { margin-bottom: 0; }
  .wr-hero__cover { order: -1; max-width: 260px; }
  .wr-hero__aside .wr-copy, .wr-hero__claim { max-width: 40ch; margin-left: auto; margin-right: auto; }

  .wr-sechead { grid-template-columns: 1fr; gap: 20px; }
  .wr-sechead__aside { text-align: left; }
  .wr-sechead__aside .wr-copy { margin-left: 0; }

  /* The two filters stay side by side and split the row, rather than the second
     dropping under the first and doubling the height of the control strip. */
  .wr-filters { flex-wrap: nowrap; gap: 12px; }
  .wr-filters__field { flex: 1 1 0; min-width: 0; gap: 6px; }
  .wr-filters__label { font-size: 12px; }
  .wr-filters .wr-select { min-width: 0; flex: 1 1 auto; width: 100%; }

  .wr-books { grid-template-columns: 1fr; }
  .wr-artgrid { grid-template-columns: 1fr; }
  .wr-byline, .wr-split { grid-template-columns: 1fr; gap: 34px; }

  .wr-about { grid-template-columns: 1fr; }
  .wr-about__copy { padding: 58px 24px; }
  .wr-about__photo, .wr-about__photo img { min-height: 380px; }
  .wr-about__photo { order: -1; }

  /* Both end-of-block buttons keep to their own text width on a phone rather
     than running the full column: "More about Henry" was reading as a banner. */
  .wr-about__copy .wr-btn,
  .wr-split .wr-btn { align-self: flex-start; width: auto; padding: 15px 26px; }

  /* The three destinations were reading as a stack of 24px serif headlines with
     44px of air around each -- taller than the CTA it sits under. On a phone it
     becomes a compact row: smaller names, tight gaps, and it wraps to two lines
     instead of three tall ones. */
  .wr-finds { gap: 2px 22px; margin-top: 26px; }
  .wr-find { gap: 9px; padding: 8px 0; }
  .wr-find svg { width: 18px; height: 18px; }
  .wr-find svg[fill="currentColor"] { width: 17px; height: 17px; }
  .wr-find__name { font-size: 17px; }
  .wr-cta__finds .wr-finds { margin-top: 12px; gap: 2px 22px; }
  .wr-cta__finds .wr-find__name { font-size: 17px; }

  .wr-cta__actions { gap: 10px; }
  .wr-cta__actions .wr-btn { flex: 1 1 100%; }
  .wr-cta__finds { margin-top: 32px; padding-top: 22px; }
}

@media (prefers-reduced-motion: reduce) {
  .wr-rise { animation: none; }
}


/* ============================================================================
   ABOUT — the profile
   ----------------------------------------------------------------------------
   A long-form magazine feature that doubles as a one-page portfolio. The story
   runs backwards — today first, then walked back to 1975 — because that is how
   you actually meet a person.

   No new hues: paper, ink, petrol blue and the one gold accent all come from
   the site tokens above. Two rules keep the page honest:
     · gold on paper is always --gold-deep. Plain --gold is legal only on the
       blue index band, where it clears contrast;
     · serif for display and quotes, sans for body, mono for labels — never
       crossed.

   No border radius and no shadow appears anywhere below. Rules and bands do all
   the separation work.

   Everything is scoped to a .ab- class this page alone uses.
   ============================================================================ */

body[data-page="about"],
body[data-page="leasted-1p"] {
  --a-serif:     'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --a-prose:     #2B251C;   /* body copy on paper — softer than ink, still 11:1 */

  /* -- The serif does FOUR jobs, not thirteen -------------------------------
     The page had thirteen serif roles across three weights, four separate
     italic treatments and thirteen hand-set sizes. Nothing was broken -- every
     face loaded -- but a reader could not tell a standfirst from a pull quote
     from a signature, because all three were the same gesture at different
     sizes. Too many voices reads as a font problem even when it is not.

     Four roles now, and every size comes from the shared display scale:

       DISPLAY  serif 500 roman   headline, section headings, next-row names
       VOICE    serif 400 italic  standfirst, pull quotes, signature
       FIGURE   serif 400 roman   dropcap, slide mark, all numerals
       LABEL    mono caps         unchanged -- it was already right

     Weight 600 and 500-italic are gone: each appeared twice and earned neither
     its own weight nor the reader's attention. */
  --a-display: 500;
  --a-figure:  400;
  /* Kept as a page-local name for readability, but it is the global token now
     -- the paper-gold problem was never specific to this page. */
  --a-gold-deep: var(--gold-deep);
  --a-on-blue-2: rgba(251,247,238,.76);
  --a-hair-blue: rgba(251,247,238,.18);
  background: var(--paper);

  /* This page uses no radius and no shadow -- rules and bands do all the
     separation work. Setting it here means the shared card obeys that rule
     without the page restyling the component. */
  --card-bg:     var(--paper2);
  --card-hover:  var(--blue);
}

/* Ring colours only. Never removed, never restyled per component. */
body[data-page="about"] .ab-band,
body[data-page="leasted-1p"] .ab-band { --focus: var(--blue); }
body[data-page="about"] .ab-band--index,
body[data-page="leasted-1p"] .ab-band--index { --focus: var(--gold); }

/* Same rule as the Writing page: the band takes the site rhythm rather than a
   number of its own, so the gap between sections does not change when a reader
   moves from About to any other page. The variants that differ do so by a
   stated amount ABOVE the token instead of restating an absolute height, which
   is also what lets the 900px breakpoint shorten this page along with the rest.
   --open and --next are open-ended on one side because they butt directly into
   the block that follows them. */
.ab-band { background: var(--paper); color: var(--a-prose); padding: var(--wrap-y) 0; }
.ab-band--open     { padding: 0; }
.ab-band--article  { padding: var(--wrap-y) 0 calc(var(--wrap-y) + 8px); }
.ab-band--index    { background: var(--blue); color: var(--paper2); }
.ab-band--words    { background: var(--paper2); }
.ab-band--next     { padding: calc(var(--wrap-y) + 8px) 0 0; scroll-margin-top: 24px; }

.ab-wrap { max-width: var(--wrap-read); margin: 0 auto; padding: 0 var(--wrap-x); }

/* -- Type ------------------------------------------------------------------ */
/* Same reason as .wr-eyebrow above -- this is a <p> wherever it is used. */
.ab-label {
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  font-weight: 400;
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--sub);
  margin: 0;
}

.ab-h {
  font-family: var(--a-serif);
  font-weight: var(--a-display);
  font-size: var(--step-3);
  line-height: 1.18;
  letter-spacing: -.005em;
  color: var(--ink);
  margin: 0;
  scroll-margin-top: 24px;
}
.ab-h--onblue { color: var(--paper2); }

.ab-body p { margin: 0 0 22px; }

/* -- 1  Masthead line ------------------------------------------------------ */
/* Currently unrendered — the page opens straight on the headline. Kept so the
   row can come back without rebuilding it. */
/* Three items on one baseline, hairline under. The only thing above the
   headline, and deliberately the smallest type on the page. */
.ab-masthead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 24px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
.ab-masthead__mid {
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--ink);
  text-align: center;
}

/* -- 2  Headline and portrait plate ---------------------------------------- */
.ab-head { padding: var(--wrap-y) 0 44px; text-align: center; scroll-margin-top: 24px; }

.ab-headline {
  font-family: var(--a-serif);
  font-weight: var(--a-display);
  font-size: var(--step-6);
  line-height: 1.04;
  letter-spacing: -.015em;
  color: var(--ink);
  max-width: 19ch;
  margin: 0 auto;
}
.ab-standfirst {
  /* VOICE. One italic gesture on this page, at three sizes. */
  font-family: var(--a-serif);
  font-style: italic;
  font-weight: var(--a-figure);
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--sub);
  max-width: 52ch;
  margin: 22px auto 0;
}

/* The portrait is a plate, not a banner: the source is a tight vertical
   headshot, so a full-bleed cover crop would cut the chin off at every width.
   The print sits centred on paper stock at the plate's full height instead,
   which keeps the whole face and reads as a magazine plate. */
.ab-plate { margin: 0; background: var(--paper2); border: 1px solid var(--line); }
/* The print is sized by ASPECT, not by a separate height clamp: the source is
   1170x1345, so a 12:13 box shows ~94% of it at every viewport width. A fixed
   height instead cropped harder as the page narrowed, which is how the chin
   kept ending up outside the frame. */
.ab-plate img {
  display: block;
  width: min(100%, 720px);
  aspect-ratio: 12 / 13;
  margin: 0 auto;
  object-fit: cover;
  object-position: 50% 25%;
}
.ab-plate__labels {
  display: flex; justify-content: space-between; gap: 20px;
  padding: 12px 16px;
  border-top: 1px solid var(--line);
}

/* -- 3  The article -------------------------------------------------------- */
/* Rail | column | notes. The two outer columns are sticky and purely
   navigational — everything that matters is readable without them. */
.ab-article {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr) 190px;
  gap: 48px;
}

/* The rail and the notes stick for the whole length of the article. Both
   columns must STRETCH to the row height for that to work — a grid item sized
   to its own content gives its sticky child nowhere to travel, which is the
   usual reason a sticky rail scrolls away on the first flick. */
.ab-rail, .ab-notes { align-self: stretch; }
.ab-rail__inner, .ab-notes__inner { position: sticky; top: 24px; }

.ab-rail__inner {
  border-top: 2px solid var(--ink);
  padding-top: 14px;
  display: flex; flex-direction: column;
}
.ab-rail__title { color: var(--ink); padding: 0 0 8px 10px; }
.ab-rail__item {
  display: block;
  padding: 11px 0 11px 10px;         /* keeps the target at 44px tall */
  text-decoration: none;
  border-top: 1px solid var(--line);
  transition: color var(--t-fast) var(--ease);
}
.ab-rail__name {
  display: block;
  /* DISPLAY, at the rail size. */
  font-family: var(--a-serif);
  font-weight: var(--a-display);
  font-size: var(--step-1);
  line-height: 1.1;
  color: var(--ink);
}
.ab-rail__when { display: block; margin-top: 3px; }
.ab-rail__item:hover .ab-rail__name { color: var(--blue); }

/* Scroll position, marked on the rail. The 2px blue edge is the signal; the
   colour shift alone would leave the state invisible to anyone who cannot see
   it, so the item also carries aria-current from the script. */
.ab-rail__item.is-current { box-shadow: inset 2px 0 0 var(--blue); }
.ab-rail__item.is-current .ab-rail__name { color: var(--blue); }
.ab-rail__item.is-current .ab-rail__when { color: var(--a-gold-deep); }

.ab-body { max-width: 66ch; font-size: 17px; line-height: 1.9; }
.ab-kicker { color: var(--a-gold-deep); margin: 0 0 16px; }

/* Drop cap: first paragraph only, and only where a float will not orphan it. */
.ab-drop::first-letter {
  /* FIGURE. The dropcap, the slide mark and the numerals are one role -- a
     figure set in the serif -- so they share a weight and differ only in size. */
  font-family: var(--a-serif);
  font-weight: var(--a-figure);
  font-size: 82px;
  line-height: .78;
  color: var(--blue);
  float: left;
  padding: 6px 14px 0 0;
}

.ab-h + .ab-date { margin: 8px 0 20px; }
.ab-body .ab-h { margin-top: 46px; }

.ab-quote {
  margin: 38px 0;
  padding: 26px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.ab-quote p {
  /* VOICE, at the pull-quote size. */
  font-family: var(--a-serif);
  font-style: italic;
  font-weight: var(--a-figure);
  font-size: var(--step-3);
  line-height: 1.4;
  color: var(--blue);
  margin: 0;
}

.ab-figure { margin: 34px 0 38px; }
.ab-figure img {
  display: block;
  width: 100%;
  height: 300px;
  object-fit: cover;
  background: var(--paper2);
}
.ab-figure figcaption { display: block; padding-top: 10px; letter-spacing: .12em; }

.ab-sign { margin-top: 44px; padding-bottom: 18px; }
.ab-sign__name {
  /* VOICE, at the signature size. */
  font-family: var(--a-serif);
  font-style: italic;
  font-weight: var(--a-figure);
  font-size: var(--step-2);
  color: var(--ink);
  margin: 0 0 6px;
}

/* Margin notes: the only gold rule on paper, and never longer than a breath. */
.ab-notes__inner { display: flex; flex-direction: column; gap: 30px; }
.ab-note {
  border-left: 2px solid var(--gold);
  padding-left: 14px;
  font-size: 14px;
  line-height: 1.7;
  color: var(--sub);
  margin: 0;
}
.ab-note .ab-label { display: block; color: var(--a-gold-deep); margin-bottom: 6px; }

/* -- 4  Index of the work --------------------------------------------------
   Currently unrendered: the four destinations moved into the closing section
   as a link column. Kept intact so the blue band can be restored. */
.ab-index__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 24px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--a-hair-blue);
}
.ab-index__count { color: var(--gold); }

.ab-entry {
  display: grid;
  grid-template-columns: 56px 1.1fr 1.4fr auto;
  gap: 28px;
  align-items: baseline;
  padding: 26px 12px 26px 0;
  border-bottom: 1px solid var(--a-hair-blue);
  text-decoration: none;
  transition: background var(--t-fast) var(--ease);
}
.ab-entry:hover { background: rgba(251,247,238,.06); }
.ab-entry__num {
  /* FIGURE, at the list-numeral size. */
  font-family: var(--a-serif);
  font-weight: var(--a-figure);
  font-size: var(--step-1);
  color: var(--gold-text);
  padding-left: 12px;
}
.ab-entry__title {
  /* DISPLAY. This is a heading, so it takes the display weight -- at 400 it
     read lighter than the section headings it sits beneath, inverting the
     hierarchy. */
  font-family: var(--a-serif);
  font-weight: var(--a-display);
  font-size: var(--step-3);
  line-height: 1.15;
  color: var(--paper2);
}
.ab-entry__desc { font-size: 15px; line-height: 1.75; color: var(--a-on-blue-2); }
.ab-entry__cta {
  font-family: var(--font-mono);
  font-size: var(--label-sm);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--gold-text);
  white-space: nowrap;
}

/* -- 5  In their words -----------------------------------------------------
   Currently unrendered. The slider script in enhance.js is guarded on the
   markup being present, so both sides simply idle while it is off. */
/* Only the active quote is in the flow — the others are [hidden], not parked
   off-screen on a translated track, so the fade can be trusted mid-stream. */
.ab-words__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px;
  margin-bottom: 26px;
}
.ab-words__controls { display: flex; gap: 10px; flex: 0 0 auto; }
/* The same circular control as .nav-btn on every other page -- this was a 46px
   square, so the "next quote" button changed shape between About and home. */
.ab-arrow {
  width: var(--control-size); height: var(--control-size);
  border-radius: 50%;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid var(--ink);
  color: var(--ink);
  font-size: 18px;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.ab-arrow:hover { background: var(--ink); color: var(--paper); }

.ab-slider {
  border: 1px solid var(--line);
  min-height: 220px;
  display: flex; flex-direction: column; justify-content: center;
}
.ab-slide {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 34px;
  align-items: start;
  padding: 56px 60px;
  margin: 0;
}
.ab-slide__mark {
  /* FIGURE, at the slide-mark size. */
  font-family: var(--a-serif);
  font-weight: var(--a-figure);
  font-size: 76px;
  line-height: .8;
  color: var(--a-gold-deep);
}
.ab-slide__quote {
  /* VOICE, at the slide size. */
  font-family: var(--a-serif);
  font-style: italic;
  font-weight: var(--a-figure);
  font-size: var(--step-2);
  line-height: 1.5;
  color: var(--ink);
  margin: 0;
}
.ab-slide__by { display: block; margin-top: 18px; }

@keyframes abSlideFade {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
.ab-slide.is-in { animation: abSlideFade .55s cubic-bezier(.22,.75,.3,1) both; }

/* Same mark as every other slider, and the button now carries a 44px target
   the way .wr-dot does -- it was a bare 34x5px bar, which is under half the
   minimum height for a touch control. The bar is drawn by ::before so the
   target can grow without the visible mark growing with it. */
.ab-dots { display: flex; gap: 2px; margin-top: 14px; }
.ab-dot {
  width: 44px; height: 44px;
  padding: 0; border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.ab-dot::before {
  content: ''; width: var(--dot); height: var(--dot); border-radius: var(--r-pill);
  background: var(--muted-2);
  transition: width var(--t-base) var(--ease), background var(--t-fast) var(--ease);
}
.ab-dot.is-active::before { width: var(--dot-active); background: var(--blue); }

/* -- 6  Where to go next ---------------------------------------------------
   Four professions, each a full-bleed row that fills with the colour its own
   page runs on. No plate, no thumbnail — the row itself is the interaction, so
   the whole thing is CSS and works on a page with no script at all.

   All four are tokens now -- olive was promoted to site scope when the homepage
   teaser needed it, so nothing here restates a hex. Every one holds paper text
   at 6:1 or better.

   Rows run edge to edge; an inner element carries the page measure, so the type
   still lines up with the article above it. */
/* Each row fills with the colour of the page it leads to -- the same hue that
   page uses for itself, so the link previews its destination. Tokens, not
   copies of the hexes, so a palette change carries through here too. */
/* Three variables per row: --g fills it on hover, --gt is the text that
   survives that fill, --gb is the bar it wears at rest.

   For three of the four, fill and bar are the same colour -- they are dark, so
   they read against paper either way. Writing's colour is a light paper rather
   than a dark ground, so it splits: the fill stays the true paper colour (and
   takes ink, since paper text on it measures 1.6:1), while the bar takes the
   text-weight olive, which reads at 7.5:1 and so matches the other three bars
   instead of vanishing at 1.6:1. */
.ab-nextrow--author     { --g: var(--olive);     --gt: var(--ink);    --gb: var(--olive-ink); }
.ab-nextrow--developer  { --g: var(--night);     --gt: var(--paper2); --gb: var(--night);     }
.ab-nextrow--consultant { --g: var(--blueHi);    --gt: var(--paper2); --gb: var(--blueHi);    }
.ab-nextrow--educator   { --g: var(--greenDeep); --gt: var(--paper2); --gb: var(--greenDeep); }

/* The four rows below name their own destinations, so the heading above them
   was saying twice what the list already says. Hidden rather than deleted --
   the markup stays in place if it is wanted back. */
.ab-next__head { display: none; }
.ab-next__lede { font-size: 17px; line-height: 1.8; color: var(--sub); margin: 14px 0 0; }

.ab-next { margin-top: 44px; border-top: 2px solid var(--ink); }

.ab-nextrow {
  display: block;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  background: transparent;
  transition: background var(--t-fast) var(--ease);
}
/* The service colour is stated at rest, not only on hover -- a reader scanning
   the four rows should be able to tell which is which without pointing at them,
   and a colour that only exists on hover is invisible on a touch screen.

   It reads as a bar on the numeral rather than a filled row: the section is the
   quiet close to an editorial page, and four solid blocks would outweigh the
   article above. Hover still fills the whole row in the same colour, so the bar
   is a preview of the fill rather than a separate idea.

   The bar sits on the inner element, which carries the page measure -- the rows
   themselves run full-bleed, so an edge-anchored bar would float far from the
   text it belongs to. */
.ab-nextrow__inner {
  max-width: var(--wrap-read);
  margin: 0 auto;
  /* The bar is absorbed into the gutter rather than added to it, so the type
     still lines up with the article above. */
  padding: 30px var(--wrap-x) 30px calc(var(--wrap-x) - var(--stripe));
  border-left: var(--stripe) solid var(--gb);
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) minmax(0, 1.15fr) auto;
  align-items: baseline;
  gap: 28px;
}
.ab-nextrow__num {
  /* FIGURE, at the list-numeral size. */
  font-family: var(--a-serif);
  font-weight: var(--a-figure);
  font-size: var(--step-1);
  color: var(--sub);
  transition: color var(--t-fast) var(--ease);
}
.ab-nextrow__name {
  /* DISPLAY, at the next-row size. */
  font-family: var(--a-serif);
  font-weight: var(--a-display);
  font-size: var(--step-4);
  line-height: 1.05;
  color: var(--ink);
  transition: color var(--t-fast) var(--ease);
}
.ab-nextrow__note {
  font-size: 15px;
  line-height: 1.75;
  color: var(--sub);
  transition: color var(--t-fast) var(--ease);
}
.ab-nextrow__dest {
  white-space: nowrap;
  color: var(--a-gold-deep);
  transition: color var(--t-fast) var(--ease);
}
.ab-nextrow__go {
  display: inline-block;
  margin-left: 6px;
  transition: transform var(--t-fast) var(--ease);
}

/* Pointer and keyboard get the same answer: the row fills with its colour. */
.ab-nextrow:hover,
.ab-nextrow:focus-visible { background: var(--g); }
/* Once the row is filled, a bar in the same colour would disappear into it, so
   it inverts to the row's own text colour and stays the identifying mark. */
.ab-nextrow:hover .ab-nextrow__inner,
.ab-nextrow:focus-visible .ab-nextrow__inner { border-left-color: var(--gt); }
.ab-nextrow:hover .ab-nextrow__name,
.ab-nextrow:focus-visible .ab-nextrow__name { color: var(--gt); }
.ab-nextrow:hover .ab-nextrow__num,
.ab-nextrow:hover .ab-nextrow__note,
.ab-nextrow:hover .ab-nextrow__dest,
.ab-nextrow:focus-visible .ab-nextrow__num,
.ab-nextrow:focus-visible .ab-nextrow__note,
.ab-nextrow:focus-visible .ab-nextrow__dest { color: var(--gt); opacity: .82; }
.ab-nextrow:hover .ab-nextrow__go,
.ab-nextrow:focus-visible .ab-nextrow__go { transform: translateX(5px); }

/* The ring is drawn inside, so it stays visible once the row is filled. */
body[data-page="about"] .ab-nextrow:focus-visible,
body[data-page="leasted-1p"] .ab-nextrow:focus-visible { outline-offset: -6px; outline-color: var(--paper2); }

/* -- Responsive ------------------------------------------------------------ */
@media (max-width: 1080px) {
  /* The notes rejoin the column as short standalone paragraphs; the rail keeps
     its own row above the article rather than squeezing the reading measure. */
  .ab-article { grid-template-columns: 1fr; gap: 38px; }
  .ab-rail__inner, .ab-notes__inner { position: static; }
  .ab-rail__inner { flex-direction: row; flex-wrap: wrap; gap: 0 28px; align-items: baseline; }
  .ab-rail__item { border-top: 0; }
  .ab-rail__name { font-size: 18px; }
  .ab-notes__inner { flex-direction: row; flex-wrap: wrap; gap: 24px; }
  .ab-note { flex: 1 1 220px; }
  .ab-body { max-width: 66ch; }

  .ab-entry { grid-template-columns: 44px 1fr; gap: 10px 20px; }
  .ab-entry__desc, .ab-entry__cta { grid-column: 2; }
  .ab-entry__num { padding-left: 0; }

  /* The note drops below the profession rather than squeezing beside it. */
  .ab-nextrow__inner { grid-template-columns: 46px minmax(0, 1fr) auto; gap: 6px 20px; }
  .ab-nextrow__note { grid-column: 2 / -1; }
}

@media (max-width: 860px) {


  .ab-masthead { flex-wrap: wrap; gap: 6px 18px; }
  .ab-masthead__mid { text-align: left; }
  .ab-head { padding: var(--wrap-y) 0 32px; }

  .ab-slide { padding: 38px 26px; grid-template-columns: 1fr; gap: 12px; }
  .ab-slide__mark { font-size: 56px; }

  .ab-nextrow__inner { padding: 22px 24px; grid-template-columns: 34px minmax(0, 1fr); gap: 6px 14px; }
  .ab-nextrow__dest { grid-column: 2; }

  /* The chapter rail is dropped on a phone. Wrapped into rows above the article
     it stopped being a rail and read as a loose pile of titles and dates with no
     structure -- and it is only a shortcut into an article the reader is already
     at the top of. The headings it points at are all still there. */
  .ab-rail { display: none; }

  /* The band above already ends on a rule, so the list's own 2px top border was
     a second line in the same place. */
  .ab-next { border-top: 0; margin-top: 32px; }
}

@media (prefers-reduced-motion: reduce) {
  .ab-slide.is-in { animation: none; }
  .ab-nextrow__go { transition: none; }
}
