/* ==========================================================================
   ASC Inborn Analytics
   Room A, UI/UX Studio
   Direction: "book jacket, not landing page". Warm paper, editorial serif,
   ridge-contour graphic system taken from the logo arch.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0 · Fonts (self-hosted variable WOFF2, no CDN, no network dependency)
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Newsreader';
  src: url('fonts/Newsreader-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Newsreader';
  src: url('fonts/Newsreader-latin-ext.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Lexend';
  src: url('fonts/Lexend-latin.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lexend';
  src: url('fonts/Lexend-latin-ext.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   1 · Tokens: primitive → semantic → component
   -------------------------------------------------------------------------- */
:root {
  /* primitive ---------------------------------------------------------- */
  --paper: #fbf7ef;
  --paper-2: #f3ecdf;
  --paper-3: #ebe2d1;
  --card: #ffffff;
  --ink: #0c1a2f;
  --ink-soft: #4a5567;
  --blue: #003d91;          /* logo */
  --blue-deep: #002454;
  --blue-tint: #e6ecf5;
  --green: #9aaf00;         /* logo */
  --green-deep: #5c6a00;
  --green-tint: #eef2d6;
  --clay: #a8481f;
  --clay-tint: #f6e7df;
  --danger: #a3231b;

  /* semantic ----------------------------------------------------------- */
  --bg: var(--paper);
  --bg-alt: var(--paper-2);
  --fg: var(--ink);
  --fg-muted: var(--ink-soft);
  --accent: var(--blue);
  --cta: var(--green);
  --cta-fg: var(--ink);
  --rule: #ddd2bd;
  --rule-soft: #e8dfcd;
  --focus: #003d91;

  /* type --------------------------------------------------------------- */
  --font-display: 'Newsreader', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --font-body: 'Lexend', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  --fs-display: clamp(2.5rem, 1.5rem + 4.6vw, 4.75rem);
  --fs-h2: clamp(1.95rem, 1.35rem + 2.6vw, 3.1rem);
  --fs-h3: clamp(1.3rem, 1.15rem + 0.8vw, 1.65rem);
  --fs-numeral: clamp(2rem, 1.4rem + 2.4vw, 3rem);
  --fs-lead: clamp(1.125rem, 1.04rem + 0.42vw, 1.35rem);
  --fs-body: clamp(1.0625rem, 1.03rem + 0.15vw, 1.125rem);
  --fs-small: 0.9375rem;
  --fs-eyebrow: 0.75rem;

  /* space --------------------------------------------------------------
     Round 3 compressed the top of the scale and the section rhythm. The
     reader is a parent on a phone in the evening: less scrolling between
     ideas. The small end (4-32px) is untouched, because that is component
     padding and touch spacing, not rhythm. Only the three large steps and
     the section band moved. */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;  --s-5: 24px;
  --s-6: 32px;  --s-7: 48px;  --s-8: 56px;  --s-9: 72px;  --s-10: 96px;

  --shell: 1200px;
  --gutter: clamp(20px, 4vw, 40px);
  --section-y: clamp(44px, 5vw, 84px);

  /* form / radius / shadow --------------------------------------------- */
  --r-sm: 6px;  --r-md: 10px;  --r-lg: 18px;  --r-pill: 999px;
  --shadow-sm: 0 1px 2px rgba(12, 26, 47, .06), 0 2px 8px rgba(12, 26, 47, .04);
  --shadow-md: 0 2px 6px rgba(12, 26, 47, .06), 0 14px 40px rgba(12, 26, 47, .09);

  --dur: 180ms;
  /* Round 3: 560ms down to 300ms. The reveal was firing late and then running
     long, so a photograph could sit as an empty frame for the better part of a
     second after the reader had already arrived at it. It is now short enough
     that, with the observer firing 15% of a viewport early (main.js), the
     content has finished arriving by the time it clears the fold. */
  --dur-reveal: 300ms;
  --ease: cubic-bezier(.22, .7, .24, 1);

  /* Ridge marker: two stacked contour waves, taken from the fingerprint arch.
     Replaces the old dash bullet on eyebrows and in-card lists. Two colourways
     because an SVG in `background-image` cannot inherit currentColor:
     `green-deep` for paper grounds (5.6:1), logo green for the dark band. */
  --mark-tilde: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='13' viewBox='0 0 20 13' fill='none' stroke='%235c6a00' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M1.6 4.4c1.7-2.4 3.5-2.4 5.2 0s3.5 2.4 5.2 0 3.5-2.4 5.2 0'/%3E%3Cpath d='M1.6 9.6c1.7-2.4 3.5-2.4 5.2 0s3.5 2.4 5.2 0 3.5-2.4 5.2 0'/%3E%3C/svg%3E");
  --mark-tilde-green: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='13' viewBox='0 0 20 13' fill='none' stroke='%239aaf00' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M1.6 4.4c1.7-2.4 3.5-2.4 5.2 0s3.5 2.4 5.2 0 3.5-2.4 5.2 0'/%3E%3Cpath d='M1.6 9.6c1.7-2.4 3.5-2.4 5.2 0s3.5 2.4 5.2 0 3.5-2.4 5.2 0'/%3E%3C/svg%3E");
}

/* --------------------------------------------------------------------------
   2 · Reset & base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.68;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--ink);
  margin: 0;
  text-wrap: balance;
}
h1 { font-size: var(--fs-display); line-height: 1.04; letter-spacing: -.022em; }
h2 { font-size: var(--fs-h2);     line-height: 1.1;  letter-spacing: -.018em; }
h3 { font-size: var(--fs-h3);     line-height: 1.22; letter-spacing: -.012em; }
h4 { font-size: 1.0625rem; line-height: 1.35; font-family: var(--font-body); font-weight: 600; letter-spacing: -.005em; }

p { margin: 0 0 var(--s-4); max-width: 62ch; }
p:last-child { margin-bottom: 0; }

a { color: var(--blue); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--clay); }

/* A link sitting inside a sentence, where the default blue would break the run of
   prose into two colours mid-line. Colour is inherited from the paragraph instead
   and the underline carries the whole distinction, thickened to 2px and tinted
   --green-deep. Same idiom as `.band-foot a` in book.html, which arrived at it for
   the same reason on a different ground. --green-deep measures 5.59:1 on --paper,
   well past the 3:1 a non-text indicator needs, where the logo --green would be
   2.31:1 and fail. Clay on hover, matching every other link on the site. */
.prose-link {
  color: inherit; font-weight: 600;
  text-decoration-color: var(--green-deep);
  text-decoration-thickness: 2px; text-underline-offset: 3px;
}
.prose-link:hover { color: var(--clay); text-decoration-color: var(--clay); }

img { max-width: 100%; display: block; }
ul, ol { margin: 0; padding: 0; }
strong { font-weight: 600; }

::selection { background: var(--green); color: var(--ink); }

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 3px;
}
/* Logo blue is the focus ring on paper. On the navy grounds it is invisible, so
   those containers hand the ring a white value instead. */
.hero, .section--dark, .site-footer, .contact-card { --focus: #ffffff; }
.skip-link {
  position: absolute; left: var(--s-4); top: -100px; z-index: 200;
  background: var(--blue); color: #fff; padding: var(--s-3) var(--s-5);
  border-radius: 0 0 var(--r-md) var(--r-md); font-weight: 600; text-decoration: none;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: 0; color: #fff; }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------------------
   3 · Layout primitives
   -------------------------------------------------------------------------- */
.shell {
  width: 100%; max-width: var(--shell); margin-inline: auto;
  padding-inline: var(--gutter);
}
.shell--wide { max-width: 1360px; }

.section { padding-block: var(--section-y); position: relative; }
.section--alt { background: var(--bg-alt); }

.section--dark { background: var(--blue-deep); color: #f2f5fa; }
.section--dark h2, .section--dark h3, .section--dark h4 { color: #fff; }
.section--dark p { color: #d3dceb; }

.section-head { max-width: 720px; margin-bottom: clamp(28px, 3vw, 44px); }
.section-head--wide { max-width: 860px; }
.section-head .lead { margin-top: var(--s-3); }
/* Multi-sentence headings need to run smaller than a single-clause h2 or they
   swallow the section they are opening. */
.h2--stack { font-size: clamp(1.7rem, 1.2rem + 1.9vw, 2.5rem); line-height: 1.14; }

/* `flex`, not `inline-flex`: as an inline-level box the eyebrow sat in an
   anonymous line box whose strut came from the 17px/1.68 body text, adding
   ~10px of leading around a 12px label. Block-level removes it, and takes the
   marker's alignment out of the parent's line box as well. */
.eyebrow {
  display: flex; align-items: flex-start; gap: var(--s-2);
  width: max-content; max-width: 100%;
  font-family: var(--font-body); font-size: var(--fs-eyebrow); font-weight: 600;
  letter-spacing: .15em; text-transform: uppercase; color: var(--green-deep);
  margin: 0 0 var(--s-3);
  /* the 62ch measure inherited from `p` is a body measure and forces the longer
     eyebrows onto two lines, which strands the marker beside a wrapped label */
}
/* Marker sizing and position are optical, not box-based. The label is set in
   caps, so the text band IS the cap band: 0.70em tall in Lexend. At 20x13 the
   marker's ink stood 9.8px against an 8.4px cap band and overhung it at the
   top, which is what read as "slightly above the text". 17x11 matches the cap
   band; the offset centres the ink on it (0.865em is the cap centre at
   line-height 1.68, and the ink sits 0.42px low inside its own box). */
.eyebrow::before {
  content: ''; width: 17px; height: 11px; flex: none;
  margin-top: calc(.865em - 5.9px);
  background: var(--mark-tilde) no-repeat center / 17px 11px;
}
.section--dark .eyebrow { color: var(--green); }
.section--dark .eyebrow::before { background-image: var(--mark-tilde-green); }

.lead { font-size: var(--fs-lead); line-height: 1.6; color: var(--ink-soft); max-width: 60ch; }
.section--dark .lead { color: #c8d3e5; }

.rule-ridge {
  height: 14px; border: 0; margin: 0;
  background: repeating-linear-gradient(90deg, var(--rule) 0 3px, transparent 3px 9px);
  opacity: .8;
}

/* --------------------------------------------------------------------------
   4 · Buttons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 48px; padding: var(--s-3) var(--s-6);
  font-family: var(--font-body); font-size: 1rem; font-weight: 600; line-height: 1.2;
  border-radius: var(--r-pill); border: 2px solid transparent;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), transform var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn--cta {
  background: var(--green); color: var(--cta-fg); border-color: var(--green);
  box-shadow: 0 2px 0 var(--green-deep);
}
.btn--cta:hover { background: #a9c000; border-color: #a9c000; color: var(--cta-fg); }

.btn--solid { background: var(--blue); color: #fff; border-color: var(--blue); }
.btn--solid:hover { background: var(--blue-deep); border-color: var(--blue-deep); color: #fff; }

.btn--ghost { background: transparent; color: var(--blue); border-color: rgba(0, 61, 145, .32); }
.btn--ghost:hover { background: var(--blue-tint); border-color: var(--blue); color: var(--blue); }

.section--dark .btn--ghost { color: #fff; border-color: rgba(255, 255, 255, .4); }
.section--dark .btn--ghost:hover { background: rgba(255, 255, 255, .1); border-color: #fff; color: #fff; }

/* Darkened off the WhatsApp brand green so white label clears 4.5:1 (5.0:1). */
.btn--wa { background: #0f8045; color: #fff; border-color: #0f8045; }
.btn--wa:hover { background: #0b6535; border-color: #0b6535; color: #fff; }

/* `.btn--reserved` stood here: the dashed, disabled "Pay online, not yet
   available" control. Round 4 replaced it with a working UPI payment, so the
   reserved state has nothing left to describe and the rule is gone with it. */

.btn--sm { min-height: 44px; padding: var(--s-2) var(--s-5); font-size: .9375rem; }
.btn--block { width: 100%; }
/* Long-labelled buttons must be allowed to wrap. `nowrap` on
   "Open WhatsApp with your message" overflows a 390px column. */
.btn--block, .form-success .actions .btn { white-space: normal; text-align: center; }
.btn svg { flex: none; }

/* --------------------------------------------------------------------------
   5 · Header
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(251, 247, 239, .92);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.site-header.is-stuck {
  border-bottom-color: var(--rule);
  box-shadow: 0 4px 24px rgba(12, 26, 47, .06);
}
.header-inner {
  display: flex; align-items: center; gap: var(--s-5);
  min-height: 76px; padding-block: var(--s-3);
}
/* The logo needs no plate on paper, only clear space. Minimum clear space =
   half the mark's height on every side.
   The lockup is split in two so the arch can self-draw: an SVG mark traced from
   the artwork (three nested arches plus the centre stem) and the original
   wordmark, cropped. Sizes below are the mark's own height, so the ink now
   stands ~50% taller than the old full-bleed PNG at the same nominal height. */
.brand { display: flex; align-items: center; flex: none; margin-right: auto; padding: var(--s-2) 0; }
.brand-lockup { display: flex; align-items: center; gap: 5px; }
.brand-mark { height: 52px; width: 52px; flex: none; stroke: var(--green); overflow: visible; }
.brand-word { height: 52px; width: auto; }
@media (max-width: 900px) { .brand-mark { height: 44px; width: 44px; } .brand-word { height: 44px; } }
@media (max-width: 560px) { .brand-mark { height: 40px; width: 40px; } .brand-word { height: 40px; } }
@media (max-width: 420px) { .brand-mark { height: 34px; width: 34px; } .brand-word { height: 34px; } }

/* Self-drawing arches. The resting state is the finished mark, so with motion
   switched off (or if the animation never runs) the logo is simply complete.
   The same is true of the wordmark: unclipped and opaque at rest. */
.brand-mark path { stroke-dasharray: 100; stroke-dashoffset: 0; }
.brand-word { clip-path: inset(0 0 0 0); }

/* One lockup, one beat. The wordmark used to slide in on its own from 690ms to
   1130ms, after every arch had already landed at 770ms, which is why it read as
   a second, snappier event bolted on rather than as part of the mark. It now
   starts while the outer arch is still drawing and finishes on 770ms, the exact
   frame the centre capsules do, so mark and word resolve together.

   It also stopped sliding. The arches lay ink from one end to the other; the
   wordmark now does the same, wiping left to right behind a hard edge that
   matches the strokes' own drawing end. Nothing moves, so nothing can look
   like it arrived from somewhere else. */
@media (prefers-reduced-motion: no-preference) {
  .brand-mark path { animation: markDraw 620ms var(--ease) both; }
  .brand-mark .s2 { animation-duration: 460ms; animation-delay: 190ms; }
  .brand-mark .s3 { animation-duration: 420ms; animation-delay: 330ms; }
  .brand-mark .s4 { animation-duration: 300ms; animation-delay: 470ms; }
  .brand-word    { animation: wordInk 550ms var(--ease) 220ms both; }
}
@keyframes markDraw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
@keyframes wordInk {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

.site-nav { display: flex; align-items: center; gap: var(--s-5); }
.site-nav a {
  font-size: .9375rem; font-weight: 500; color: var(--ink-soft);
  text-decoration: none; padding: var(--s-2) 0; position: relative;
  transition: color var(--dur) var(--ease);
}
.site-nav a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--green); transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.site-nav a:hover { color: var(--ink); }
.site-nav a:hover::after { transform: scaleX(1); }

@media (max-width: 1000px) { .site-nav { display: none; } }
@media (max-width: 560px) {
  .header-inner { gap: var(--s-3); min-height: 66px; }
  .header-cta { padding-inline: var(--s-4); font-size: .9375rem; }
}

/* --------------------------------------------------------------------------
   5b · Mobile menu (below 1000px only)

   `.site-nav` is switched off at 1000px and until now nothing replaced it: the
   only header control on a phone was the CTA, so the five section anchors were
   unreachable from the header on the device this site is built for.

   Everything in this block is inside a max-width query, and the three new
   elements are `display: none` at every width above it. A `display: none` flex
   item creates no box and no gap, so above 1000px the header is byte-for-byte
   the layout it was: brand, nav, CTA. Nothing here can reach it.

   Structure: the button lives in `.header-inner` after the CTA, which is both
   its visual position and its tab position. The panel is a child of
   `.site-header` and sits at `top: 100%`, so it hangs off the bottom edge of
   the bar with no measured height anywhere. The scrim has to sit OUTSIDE the
   header: `.site-header` carries `backdrop-filter`, which makes it the
   containing block for fixed-position descendants, so a `position: fixed`
   scrim inside it would be trapped in the 74px header box.
   -------------------------------------------------------------------------- */
.nav-toggle,
.nav-panel,
.nav-scrim { display: none; }

@media (max-width: 1000px) {
  /* 44x44 exactly: the minimum touch target, and the icon is the thing that
     identifies the control, so the ring around it is the same low-contrast
     ghost boundary `.btn--ghost` already uses. */
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 44px; height: 44px; padding: 0;
    background: transparent; color: var(--blue);
    border: 2px solid rgba(0, 61, 145, .32); border-radius: var(--r-pill);
    cursor: pointer;
    transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
  }
  .nav-toggle:hover { background: var(--blue-tint); border-color: var(--blue); }
  .nav-toggle svg { width: 22px; height: 22px; flex: none; }

  /* The three bars fold into a close cross while the panel is open. The button
     is the one control that stays lit inside the trap and it is the first Tab
     stop there, so it has to say what it now does; `aria-expanded` says that to
     a screen reader and this says it to the eye. The middle bar goes out and the
     outer two swing onto the diagonal about the icon's own centre, so it is the
     same three strokes rotated, not a second drawing swapped in.

     Order matters: `rotate() translateY()` moves the bar to the centre first and
     then turns it there. The reverse order turns it first and lands it off
     centre. `transform-box` is set because without it the origin is the SVG user
     space origin rather than the glyph.

     180ms, the interaction duration, not the 300ms the panel takes: the control
     answers the press at once and the surface follows it out. */
  .nav-toggle svg path {
    transform-box: view-box; transform-origin: 12px 12px;
    transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
  }
  .nav-open .nav-toggle svg path:nth-child(1) { transform: rotate(45deg) translateY(5px); }
  .nav-open .nav-toggle svg path:nth-child(2) { opacity: 0; }
  .nav-open .nav-toggle svg path:nth-child(3) { transform: rotate(-45deg) translateY(-5px); }

  /* The panel is a paper sheet, not a dark drawer, so the focus ring stays the
     blue one the rest of the header uses. `display` is what opens and closes it:
     closed, it is out of the layout, out of the accessibility tree and cannot
     add a pixel of scrollable overflow to an 11,000px document. */
  .nav-panel {
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
    box-shadow: var(--shadow-md);
    padding-block: var(--s-2);
    max-height: calc(100vh - 74px);
    max-height: calc(100dvh - 74px);
    overflow-y: auto;
    overscroll-behavior: contain;

    /* The sheet slides out from under the bar, and nothing else moves.
       `clip-path` is the mechanism rather than a transform because the five rows
       are the thing being read: a panel that also travels makes them arrive
       twice. Clipping uncovers them top to bottom in one pass, which is the
       sequence a per-item stagger would only imitate, at five times the cost.

       The insets are negative on three sides so the drop shadow, which lies
       outside the border box, sits inside the clip once the panel is open.
       `inset(0 0 0 0)` clips to the border box, which would delete that shadow
       for good. The top inset stays 0, so the sheet is cut exactly at the bar's
       lower edge and can never paint over it on the way out.

       `display` is what shows and hides the panel and `display` is discrete, so
       the transition needs `allow-discrete` to keep the panel in the layout
       while it closes, and `@starting-style` to give the opening transition a
       state to start from. A browser with neither falls back to pass 1's
       instant open, which is correct, just unanimated. `transition-behavior`
       is written after the shorthand because the shorthand resets it. */
    clip-path: inset(0 -56px 100% -56px);
    transition: clip-path var(--dur) var(--ease), display var(--dur) var(--ease);
    transition-behavior: allow-discrete;
  }
  /* Out in the reveal duration, back in the interaction duration. Opening is the
     site showing you something and closing is you dismissing it; a dismissal
     that takes as long as the reveal reads as the menu resisting. */
  .nav-open .nav-panel {
    display: block;
    clip-path: inset(0 -56px -56px -56px);
    transition: clip-path var(--dur-reveal) var(--ease), display var(--dur-reveal) var(--ease);
    transition-behavior: allow-discrete;
  }
  @starting-style {
    .nav-open .nav-panel { clip-path: inset(0 -56px 100% -56px); }
  }

  .nav-panel a {
    display: flex; align-items: center; gap: var(--s-3);
    min-height: 56px; padding: var(--s-2) var(--gutter);
    font-size: var(--fs-body); font-weight: 500; line-height: 1.3;
    color: var(--ink); text-decoration: none;
    transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
  }
  /* The same ridge marker the in-card lists use, so the menu reads as part of
     the site rather than as a browser widget dropped on top of it. */
  .nav-panel a::before {
    content: ''; width: 17px; height: 11px; flex: none;
    background: var(--mark-tilde) no-repeat center / 17px 11px;
  }
  .nav-panel a + a { border-top: 1px solid var(--rule-soft); }
  .nav-panel a:hover { background: var(--paper-2); color: var(--blue); }
  .nav-panel a:active { background: var(--paper-3); }
  /* Inset, or the ring is clipped by the panel's own edge on a full-bleed row. */
  .nav-panel a:focus-visible { outline-offset: -3px; }

  /* Above the page, below the header (z-index 100) and below the skip link
     (200), so the bar and the panel stay lit while everything else recedes. */
  .nav-scrim {
    position: fixed; inset: 0; z-index: 90;
    background: rgba(12, 26, 47, .42);
  }
  .nav-open .nav-scrim { display: block; }
  /* The bar is 92% paper over a blur. With the scrim behind it, 8% of that scrim
     came through and the bar read a shade cooler than the panel hanging off it,
     which put a seam across the top of the menu. While the menu is open the bar
     is simply paper, the same paper the panel is. */
  .nav-open .site-header { background: var(--paper); }

  /* Locking the page, and this one has exactly one right answer.

     `body { overflow-x: hidden }` already propagates to the viewport, and while
     it propagates the body is itself treated as `overflow: visible`. Adding
     `overflow: hidden` here rides that same propagation: the viewport stops
     scrolling and the body stays a non-scroller, so `.site-header` goes on
     sticking to the viewport.

     Locking `html` instead does not work, and fails in a way that is easy to
     ship: a non-visible `html` cancels the propagation, the body becomes the
     scroll container, the sticky header re-sticks to a body whose scrollTop is
     0, and the whole bar - button, panel and all - jumps to the top of the
     document and off the screen. Measured, at scrollY 600, the panel landed at
     top: -528. `position: fixed` on the body does the same thing for the same
     reason. */
  .nav-open body { overflow: hidden; }

  /* A sticky bar over a section whose heading starts 44px in leaves the heading
     under the bar when an anchor lands. Only below 1000px; scroll offsets on
     the desktop layout are untouched. */
  .section[id] { scroll-margin-top: 84px; }

  /* No script: no dead control. The button goes away and the panel is dropped
     into the flow of the header, which is the same fallback `.js-off` gives the
     payment panel on book.html. The header stops sticking, because a static
     five-item list stuck to the top of a phone screen would cost more than it
     returns - and `is-stuck` is a script state anyway, so without script the
     bar has nothing to say when the page moves. */
  .js-off .nav-toggle { display: none; }
  .js-off .site-header { position: static; }
  .js-off .nav-panel {
    display: block; position: static;
    box-shadow: none; padding-block: 0;
    border-top: 1px solid var(--rule-soft);
    max-height: none;
  }
}

/* The header runs out of room before the phone does. Measured at 15px Lexend
   with the site's own fonts loaded: brand 119.4 + CTA 161.6 + a 12px gap needs
   293px, and a 44px button with its gap needs 56 more. The content box is 350px
   at 390 and 335px at 375, so the bar fits at 390 with 13px to spare and
   overflows at 375. Trimming the CTA and stepping the lockup down one notch
   buys 30px, which carries it to 360 - the common Android width - with room
   left. */
@media (max-width: 400px) {
  .header-cta { padding-inline: var(--s-3); font-size: .875rem; }
  .brand-mark { height: 30px; width: 30px; }
  .brand-word { height: 30px; }
}
/* At 340px and below the bar already overflowed before this round - 13px at 320,
   silently, because the body clips horizontal overflow. The wordmark stands down
   there so the mark, the CTA and the menu all fit rather than one of them being
   cut off the right-hand edge. The arch mark alone is the logo's own device, and
   the brand link is labelled in text either way. */
@media (max-width: 340px) {
  .brand-word { display: none; }
}

/* --------------------------------------------------------------------------
   6 · Hero
   -------------------------------------------------------------------------- */
/* The hero ground is blue at the owner's request. That is also the one place on
   a light-ground page where the logo green can carry text legibly (6.2:1), so
   the h1 accent moves from a green underline on blue type to green type. */
.hero {
  position: relative; overflow: hidden;
  padding-block: clamp(34px, 4vw, 58px) clamp(34px, 4vw, 58px);
  background: linear-gradient(168deg, #013573 0%, var(--blue-deep) 56%, #001a3d 100%);
  color: #d3dceb;
}
/* Static ridge watermark. It is the no-script fallback: once the drawn ridge
   field exists (section 15) it stands down, because two ridge systems on the
   same ground is noise. */
.hero::before {
  content: ''; position: absolute; inset: -12% -20% auto auto;
  width: min(780px, 88vw); aspect-ratio: 1600 / 560;
  background: url('img/ridge-field-light.svg') no-repeat center / contain;
  opacity: .8; pointer-events: none;
  transition: opacity 600ms var(--ease);
}
.hero.has-field::before { opacity: 0; }
.hero > .shell { position: relative; z-index: 1; }
/* The eyebrow is lifted out of the copy column and given its own grid row, so
   the photograph's top edge lands on the h1's top edge exactly, and stays
   there when the eyebrow wraps to two lines at some widths. A margin on the
   figure would have to guess that. */
.hero-grid {
  position: relative; display: grid;
  column-gap: clamp(28px, 4vw, 56px); row-gap: 0;
  /* 1.12/.88, not the old 1.08/.92: at this measure the h1 sets in four lines
     rather than five, which is both a better shape for it and 79px less column
     for the photograph's side to make up. */
  grid-template-columns: minmax(0, 1.12fr) minmax(0, .88fr);
  grid-template-areas:
    "eyebrow ."
    "copy    figure";
  align-items: start;
}
/* Scoped to `.hero-grid` on purpose: `book.html` reuses `.hero-copy` inside its
   own `.book-grid`, and an unscoped `grid-area` would place it into an area
   that grid does not have. */
.hero-grid > .hero-eyebrow { grid-area: eyebrow; }
.hero-grid > .hero-copy    { grid-area: copy; }
.hero-grid > .hero-figure  { grid-area: figure; }
@media (max-width: 900px) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "eyebrow" "copy" "figure";
    row-gap: 0;
  }
  .hero-grid > .hero-figure { margin-top: var(--s-6); }
}

.hero .eyebrow { color: var(--green); }
.hero .eyebrow::before { background-image: var(--mark-tilde-green); }
.hero h1 { color: #fff; margin-bottom: var(--s-5); }
.hero h1 em { font-style: normal; color: var(--green); }
.hero-copy p { color: #cdd8ea; font-size: var(--fs-lead); line-height: 1.6; }
.hero-actions {
  display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-6);
}
.hero-note {
  margin-top: var(--s-5); font-size: .9375rem; font-weight: 500; color: #e2e9f4;
  display: flex; align-items: flex-start; gap: var(--s-2);
}
.hero-note svg { flex: none; margin-top: .28em; color: var(--green); }
.hero .btn--ghost { color: #fff; border-color: rgba(255, 255, 255, .42); }
.hero .btn--ghost:hover { background: rgba(255, 255, 255, .1); border-color: #fff; color: #fff; }

/* The overlaid fingertip specimen plate is gone at the owner's request: the new
   photograph carries the hero on its own and the plate was sitting on top of
   two of the four faces in it. */
.hero-figure { position: relative; }
.hero-figure .frame {
  position: relative; margin: 0; border-radius: var(--r-lg); overflow: hidden;
  box-shadow: 0 4px 10px rgba(0, 12, 34, .3), 0 24px 60px rgba(0, 12, 34, .34);
  background: var(--paper-3); aspect-ratio: 4 / 3;
}
.hero-figure .frame > img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 42%;
  filter: saturate(.9) contrast(1.02) sepia(.1);
}
.hero-figure-note {
  margin: var(--s-4) 0 0; font-size: .9375rem; line-height: 1.55; color: #c2cfe4;
  max-width: none;
}
/* What the column carries under the photograph, so the two columns finish on
   roughly the same line instead of the picture leaving a hole beneath it.
   Three facts, all of them already stated elsewhere on this page. */
.hero-figure-facts {
  list-style: none; margin: var(--s-4) 0 0; padding-top: var(--s-5);
  border-top: 1px solid rgba(255, 255, 255, .2);
  display: grid; gap: var(--s-4);
}
.hero-figure-facts li {
  position: relative; padding-left: 28px;
  font-size: .9375rem; line-height: 1.5; color: #dbe4f2;
}
.hero-figure-facts li::before {
  content: ''; position: absolute; left: 0; top: calc(.775em - 5.9px);
  width: 17px; height: 11px;
  background: var(--mark-tilde-green) no-repeat center / 17px 11px;
}
@media (max-width: 900px) { .hero-figure-facts { max-width: 46ch; } }

.hero-kicker {
  margin-top: clamp(28px, 3.4vw, 48px); padding-top: var(--s-6);
  border-top: 1px solid rgba(255, 255, 255, .2);
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-6);
  align-items: start;
}
@media (max-width: 820px) { .hero-kicker { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); } }
.hero-kicker blockquote {
  /* pulled up so its cap height, not its line box, levels with the rule above
     the fact strip alongside it */
  margin: -.22em 0 0; font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.5rem, 1.1rem + 1.7vw, 2.25rem); line-height: 1.25;
  letter-spacing: -.015em; color: #fff; text-wrap: balance;
}
.hero-kicker blockquote span { color: var(--green); }

/* Fact strip. Two things keep the four supporting lines on one baseline: each
   item is a subgrid of the strip's own rows, so the label row is one height for
   all four however many lines any of them takes; and in the four-column layout
   "Career choices" is broken over two lines like the other three, so the row is
   not two lines tall with a one-line label sitting in it. min-height is the
   fallback for a browser without subgrid. */
.facts {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--s-5); row-gap: var(--s-5); list-style: none;
}
/* Four columns only where four columns fit. The strip sits in half the shell,
   so four columns need about 113px each before the labels start taking three
   lines; that is a 1160px viewport. Below it the strip is two columns and the
   labels have room. */
@media (min-width: 1160px) { .facts { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 420px) { .facts { grid-template-columns: minmax(0, 1fr); row-gap: var(--s-4); } }
.facts li {
  border-top: 2px solid var(--green); padding-top: var(--s-3);
  display: grid; grid-row: span 2; grid-template-rows: subgrid; row-gap: var(--s-2);
}
.facts b {
  display: block; font-family: var(--font-display); font-weight: 600;
  font-size: 1.1875rem; line-height: 1.2; color: #fff; letter-spacing: -.01em;
  text-wrap: balance;
}
/* "Career choices" is the one label that fits on a single line, which dropped
   its supporting text a whole line clear of the other three. In the four-column
   layout the second word is promoted to its own line; in two columns or one,
   where the labels have room, it wraps naturally, so nothing is stranded at a
   width the break was not written for. */
@media (min-width: 1160px) {
  .facts b { min-height: 2.4em; }
  .facts .fact-line { display: block; }
}
/* `li > span`, not `span`: the supporting line is a direct child, and the label
   now carries a span of its own. */
.facts li > span { display: block; align-self: start; font-size: .8125rem; color: #b8c6dd; line-height: 1.5; }

/* --------------------------------------------------------------------------
   7 · Age bands
   -------------------------------------------------------------------------- */
.bands { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); }
@media (max-width: 900px) { .bands { grid-template-columns: minmax(0, 1fr); gap: var(--s-6); } }

.band { display: flex; flex-direction: column; }
.band-media {
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  aspect-ratio: 3 / 4; background: var(--paper-3); margin-bottom: var(--s-4);
}
@media (max-width: 900px) { .band-media { aspect-ratio: 1 / 1; } }
.band-media img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 34%;
  filter: saturate(.88) contrast(1.02) sepia(.12);
  transition: transform 500ms var(--ease);
}
.band:hover .band-media img { transform: scale(1.035); }
/* The age label used to sit inside the photograph. It is now the band's heading,
   set above the image on its own rule. */
.band-age {
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-h3);
  line-height: 1.2; letter-spacing: -.012em; color: var(--blue);
  margin: 0 0 var(--s-3); padding-bottom: var(--s-2);
  border-bottom: 2px solid var(--green);
}
.band > p { color: var(--ink-soft); font-size: var(--fs-small); }
.band-list { list-style: none; margin-top: var(--s-4); display: grid; gap: var(--s-3); }
.band-list li {
  position: relative; padding-left: 26px; font-size: var(--fs-small); line-height: 1.5;
}
/* Optical centring on the first line's cap band, not on the line box. In Lexend
   the cap centre of a 1.5 line-height line sits at .775em from the top of the
   line; the marker's ink centre is 5.9px down its own 11px box. */
.band-list li::before {
  content: ''; position: absolute; left: 0; top: calc(.775em - 5.9px);
  width: 17px; height: 11px;
  background: var(--mark-tilde) no-repeat center / 17px 11px;
}
.band-foot {
  margin-top: var(--s-7); padding: var(--s-5) var(--s-6);
  background: var(--green-tint); border-radius: var(--r-md);
  font-size: var(--fs-small); color: #3f4a13; display: flex; gap: var(--s-3); align-items: flex-start;
}
.band-foot svg { flex: none; color: var(--green-deep); margin-top: 2px; }
/* Was an inline `style="color:#3f4a13;font-weight:600"` in book.html. Moved here
   as `inherit`, which is the same colour without a second copy of the hex: the
   bar sets #3f4a13 on itself and that measures 8.3:1 on --green-tint, where
   --blue-deep-on-tint would have been the only other olive-safe option and
   --green-deep only 5.2:1. The link is therefore not distinguished by colour at
   all, so the underline is the distinction and it is thickened and tinted
   --green-deep (5.2:1 on the tint, well past the 3:1 a non-text indicator
   needs). Clay on hover, matching every other link on the site. */
.band-foot a {
  color: inherit; font-weight: 600;
  text-decoration-color: var(--green-deep);
  text-decoration-thickness: 2px; text-underline-offset: 3px;
}
.band-foot a:hover { color: var(--clay); text-decoration-color: var(--clay); }

.figcap {
  margin-top: var(--s-2); font-size: .75rem; color: var(--ink-soft);
  letter-spacing: .04em; text-transform: uppercase;
}
.section--dark .figcap { color: #9fb0c9; }

/* --------------------------------------------------------------------------
   8 · Trust ("How we know what we know"), dark band
   -------------------------------------------------------------------------- */
/* The oversized arch watermark that used to sit behind this list has been
   removed at the owner's request. What replaces it is a drawn whorl: the
   pattern class claim 02 names, traced as contours (section 15) rather than
   stamped as a graphic, sized and placed to sit behind the numbered list
   without ever getting between the reader and a sentence. */
.trust { position: relative; overflow: hidden; }
.trust > .shell { position: relative; z-index: 1; }
.trust .ridge-field {
  /* the top right of this band is the one large empty field on the page; the
     whorl goes there and bleeds off the edge, rather than sitting under the
     sentences */
  inset: -104px -15% auto auto;
  width: min(760px, 64%); height: min(760px, 64vw);
  -webkit-mask-image: radial-gradient(closest-side, #000 46%, transparent 98%);
  mask-image: radial-gradient(closest-side, #000 46%, transparent 98%);
}
@media (max-width: 940px) {
  .trust .ridge-field { inset: -90px -24% auto auto; width: 96%; height: 96vw; }
}
.trust .ridge-field .r-minor { stroke: rgba(255, 255, 255, .21); }
.trust .ridge-field .r-key   { stroke: rgba(154, 175, 0, .42); }
.trust .ridge-glow {
  background: radial-gradient(46% 46% at 52% 50%, rgba(154, 175, 0, .14), rgba(154, 175, 0, 0) 72%);
}
.trust-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: clamp(26px, 3.4vw, 52px);
  align-items: start;
}
@media (max-width: 940px) { .trust-grid { grid-template-columns: minmax(0, 1fr); } }

.trust-figure { position: sticky; top: 110px; }
@media (max-width: 940px) { .trust-figure { position: static; } }
.trust-figure .frame {
  border-radius: var(--r-lg); overflow: hidden; aspect-ratio: 3 / 4; background: #01142f;
}
/* A portrait, not a macro, so the heavy grayscale knock-back the ridge photograph
   carried would read as grim here. Warm grade, held slightly back off full
   brightness so it sits in the dark band rather than punching out of it. */
.trust-figure img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 32%;
  filter: saturate(.82) contrast(1.04) sepia(.16) brightness(.96);
}

/* The numerals used to sit in a 68px column, leaving 40px of dead air between
   the number and the sentence it belongs to. Tightened, and dropped so the
   numeral baseline lands on the heading's baseline. */
.claims { list-style: none; display: grid; gap: 0; counter-reset: c; }
.claims li {
  counter-increment: c; display: grid; grid-template-columns: 40px minmax(0, 1fr);
  gap: var(--s-3); padding-block: var(--s-5);
  border-top: 1px solid rgba(255, 255, 255, .16);
}
.claims li:first-child { border-top: 0; padding-top: 0; }
@media (max-width: 560px) { .claims li { grid-template-columns: 34px minmax(0, 1fr); gap: var(--s-3); } }
.claims li::before {
  content: '0' counter(c);
  font-family: var(--font-display); font-size: 1.375rem; font-weight: 500;
  color: var(--green); line-height: 1.48; font-variant-numeric: tabular-nums;
}
@media (max-width: 560px) { .claims li::before { font-size: 1.125rem; line-height: 1.75; } }
.claims h3 { font-size: 1.25rem; margin-bottom: var(--s-2); line-height: 1.3; }
.claims p { font-size: var(--fs-small); }

/* "Who sits across the table". Runs the full width of the band rather than
   sitting in the right-hand column, which used to leave the left side empty
   below the sticky photograph.
   It was a plain outlined rectangle; it is now a plate. Lit ground, a drawn
   arch field behind the copy, two registration ticks and a hairline running
   out of the arch mark, all from the same ridge vocabulary as the rest. */
.analyst-band {
  position: relative; overflow: hidden;
  margin-top: clamp(30px, 3.4vw, 52px); display: grid;
  grid-template-columns: minmax(0, .62fr) minmax(0, 1.38fr);
  gap: clamp(20px, 2.6vw, 36px); align-items: center;
  background:
    linear-gradient(148deg, rgba(255, 255, 255, .11) 0%, rgba(255, 255, 255, .05) 46%, rgba(255, 255, 255, .02) 100%);
  border: 1px solid rgba(154, 175, 0, .45);
  border-radius: var(--r-lg); padding: clamp(20px, 2.6vw, 32px);
}
@media (max-width: 860px) { .analyst-band { grid-template-columns: minmax(0, 1fr); } }

/* Registration ticks along the top edge. Enough to read as a plate that has
   been squared up, not enough to become a frame. */
.analyst-band::before, .analyst-band::after {
  content: ''; position: absolute; top: 9px; z-index: 2; width: 26px; height: 26px;
  border: 1px solid rgba(154, 175, 0, .8); border-bottom: 0; pointer-events: none;
}
.analyst-band::before { left: 9px; border-right: 0; border-radius: 5px 0 0 0; }
.analyst-band::after { right: 9px; border-left: 0; border-radius: 0 5px 0 0; }

/* A whorl in the corner of the plate, drawn on when the band arrives: a core
   reading as the seal on a signed sheet. Circular fade, so it never reaches
   the copy and never needs to be argued with for contrast. */
.analyst-band .ridge-field {
  inset: auto -62px -66px auto; width: 320px; height: 320px;
  -webkit-mask-image: radial-gradient(closest-side, #000 46%, transparent 100%);
  mask-image: radial-gradient(closest-side, #000 46%, transparent 100%);
}
@media (max-width: 560px) { .analyst-band .ridge-field { width: 240px; height: 240px; } }
.analyst-band .ridge-field .r-minor { stroke: rgba(255, 255, 255, .21); }
.analyst-band .ridge-field .r-key   { stroke: rgba(154, 175, 0, .44); }
.analyst-band .ridge-glow { background: none; }

/* The band used to be sized around a 4:5 portrait in a .82fr column, which made
   it the tallest thing in the section. The column is narrower and the frame is
   square, so the photograph is a portrait tile rather than the structural
   member. The file is untouched; this is a CSS reframe. */
.analyst-photo {
  position: relative; z-index: 1;
  margin: 0; border-radius: var(--r-md); overflow: hidden;
  aspect-ratio: 1 / 1; background: #01142f;
}
.analyst-photo img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 24%;
  filter: saturate(.9) contrast(1.02);
}
/* Round 4, not requested, and flagged. The mobile override was `4 / 5` with
   `object-position: 50% 22%`, and the source file is 960x1200, which IS 4/5.
   `object-fit: cover` therefore cropped nothing at all below 860px and the
   whole frame showed, including the gold desk nameplate that reads
   "CAREER COUNSELOR" in American spelling against "Counsellor" everywhere else
   on this site. Measured on the file: the plate's top edge is at 85.6% of the
   image height. The square desktop crop clears it by about two pixels, which is
   why nobody saw this on a laptop; the phone, which is this site's main device,
   showed it in full and legibly.
   The frame is now square at every width, with the crop pulled up to leave a
   real margin (visible slice ends at 83.2%) rather than a two-pixel one. The
   file is untouched: this is CSS only, and it applies the same decision the
   owner already took this round about the same nameplate in the same room. */
@media (max-width: 860px) {
  .analyst-photo img { object-position: 50% 16%; }
}
.analyst-copy { position: relative; z-index: 1; }
/* Round 4, second pass. The hairline is gone at the owner's request and the arch
   mark moves from under the text to beside it.
   `align-items: center` and not `flex-start`: the mark is the badge on a
   two-line lockup, so it centres on the pair. Aligning it to the first line
   would make it read as the heading's own bullet and leave "Click to know more"
   hanging off the bottom of it, which is the thing the owner was looking at.
   Centring also survives the heading wrapping to two lines, which it does below
   about 1100px in the .62/1.38 grid and again at 390px in one column: the mark
   stays on the optical middle of a three-line block instead of drifting.
   `flex: none` on the mark and `min-width: 0` on the text so a long heading
   wraps rather than squeezing the mark. */
.analyst-head {
  display: flex; align-items: center; gap: var(--s-4);
  margin: 0 0 var(--s-4);
}
.analyst-head-text { min-width: 0; }
.analyst-head .mark {
  width: 34px; height: 34px; flex: none; color: var(--green);
  align-self: center;
}
@media (max-width: 480px) {
  .analyst-head { gap: var(--s-3); }
  .analyst-head .mark { width: 28px; height: 28px; }
}
.analyst-copy h3 { font-size: var(--fs-h3); margin-bottom: var(--s-1); }
/* Heading and link read as one two-line block, so the gap between them is 4px
   and the block carries its bottom margin on `.analyst-head`, not here. */
.analyst-more {
  margin: 0; font-size: .9375rem; line-height: 1.4;
}
/* White, not logo green. Green on this plate measures 4.49:1 against the lit
   end of its own gradient, which is under AA for a 15px label; white is 11.1:1.
   The green stays as the underline and the arrow's resting colour, so the link
   is still brand-coloured without carrying the contrast on the type.
   inline-flex with padding-block clears the 24px target minimum of WCAG 2.2
   2.5.8: this is a standalone link, not one inline in a sentence. */
.analyst-more a {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding-block: 3px; color: #fff; font-weight: 500;
  text-decoration: underline; text-decoration-color: rgba(154, 175, 0, .85);
  text-decoration-thickness: 2px; text-underline-offset: 5px;
}
.analyst-more a svg { flex: none; color: var(--green); transition: transform var(--dur) var(--ease); }
.analyst-more a:hover { color: #fff; text-decoration-color: var(--green); }
.analyst-more a:hover svg { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) { .analyst-more a:hover svg { transform: none; } }
/* `.analyst-rule` and its hairline ::after are deleted: the "green line" the
   owner asked us to remove. The mark it carried lives on in `.analyst-head`. */
.analyst-copy p { font-size: var(--fs-small); }
.analyst-id {
  margin-top: var(--s-4); padding-top: var(--s-3);
  border-top: 1px solid rgba(255, 255, 255, .18);
}
.analyst-id span { display: block; }
.analyst-name {
  font-family: var(--font-display); font-weight: 600; font-size: 1.25rem;
  color: #fff; letter-spacing: -.012em; margin-bottom: 2px;
}
.analyst-role {
  font-size: .8125rem; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--green);
}
.analyst-org { font-size: .8125rem; color: #b7c4d9; margin-top: 3px; }

/* --------------------------------------------------------------------------
   9 · What we do
   -------------------------------------------------------------------------- */
.steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); }
@media (max-width: 860px) { .steps { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); } }
/* The 1·2·3 discs are gone at the owner's request. The green top rule keeps the
   three cards reading as a sequence without numbering them. */
.step {
  background: var(--card); border: 1px solid var(--rule-soft); border-radius: var(--r-lg);
  border-top: 3px solid var(--green);
  padding: clamp(24px, 3vw, 36px); position: relative;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.step:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.step h3 { font-size: 1.3rem; margin-bottom: var(--s-3); color: var(--blue); }
.step p { font-size: var(--fs-small); color: var(--ink-soft); }
.step-meta {
  margin-top: var(--s-4); padding-top: var(--s-3); border-top: 1px dashed var(--rule);
  font-size: .8125rem; font-weight: 500; color: var(--green-deep);
  letter-spacing: .04em; text-transform: uppercase;
}

/* Direction band: the four statements, then the wide counselling photograph.
   Sized to the 5:2 source so the picture is never cropped into a letterbox.
   It closes the section directly after the three step cards, so the gap above
   it is a step up from the 32px grid gap rather than a full section break. */
.direction-band {
  margin-top: clamp(28px, 3.2vw, 44px);
  background: var(--paper-2); border: 1px solid var(--rule-soft);
  border-radius: var(--r-lg); padding: clamp(20px, 2.6vw, 32px);
}
.direction-lines {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3) clamp(24px, 4vw, 56px); margin: 0 0 clamp(20px, 2.2vw, 28px);
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.15rem, 1rem + .7vw, 1.5rem); line-height: 1.28;
  letter-spacing: -.014em; color: var(--blue);
}
@media (max-width: 720px) { .direction-lines { grid-template-columns: minmax(0, 1fr); } }
.direction-lines span {
  display: block; position: relative; padding-left: 30px;
}
/* Newsreader, line-height 1.28: the cap centre of the first line sits at
   .515em from the top of the line, and the marker's ink centre is 7px down its
   own 13px box. Written as a calc so it holds as the heading scales with the
   viewport instead of drifting the way a flat .34em did. */
.direction-lines span::before {
  content: ''; position: absolute; left: 0; top: calc(.515em - 7px);
  width: 20px; height: 13px;
  background: var(--mark-tilde) no-repeat center / 20px 13px;
}
/* The source is 1800x1012, a shade wider than 16:9. In the 5:2 desktop box
   `cover` throws away 29% of the height, and at the old 50% 40% that came off
   the top and took the tops of two heads with it: the crop is pulled up to 14%
   so the loss lands on the desk instead. Under 620px the box becomes the
   source's own ratio, because a 3:2 box cropped 8% off each side, which is
   where the father and the counsellor are. */
.direction-media {
  margin: 0; border-radius: var(--r-md); overflow: hidden;
  aspect-ratio: 5 / 2; background: var(--paper-3);
}
@media (max-width: 620px) { .direction-media { aspect-ratio: 16 / 9; } }
.direction-media img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 14%;
  filter: saturate(.88) contrast(1.02) sepia(.12);
}

/* --------------------------------------------------------------------------
   10 · The report
   -------------------------------------------------------------------------- */
.report-grid {
  display: grid; grid-template-columns: minmax(0, .78fr) minmax(0, 1.22fr);
  gap: clamp(26px, 3.4vw, 52px); align-items: start;
}
@media (max-width: 940px) { .report-grid { grid-template-columns: minmax(0, 1fr); } }

.report-aside { position: sticky; top: 110px; }
@media (max-width: 940px) { .report-aside { position: static; } }
.report-aside .frame {
  position: relative;
  border-radius: var(--r-lg); overflow: hidden; aspect-ratio: 3 / 2;
  box-shadow: var(--shadow-md); background: var(--paper-3);
}
.report-aside .frame > img {
  width: 100%; height: 100%; object-fit: cover;
  filter: saturate(.82) contrast(1.03) sepia(.14);
}
/* The `.page-print` SVG that used to be drawn over this frame is gone. It was a
   stand-in for a photograph of a real report spread; the photograph is here
   now, with its own pages printed, and drawing furniture on top of it was
   drawing over the thing itself. */
.report-aside-note {
  margin-top: var(--s-4); font-size: .9375rem; color: var(--ink-soft);
}

.toc { list-style: none; counter-reset: t; }
.toc li {
  display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: var(--s-5);
  padding-block: var(--s-5); border-top: 1px solid var(--rule);
}
.toc li:first-child { border-top: 0; padding-top: 0; }
@media (max-width: 620px) {
  .toc li { grid-template-columns: minmax(0, 1fr); gap: var(--s-2); padding-block: var(--s-5); }
}
.toc-range {
  font-family: var(--font-display); font-size: var(--fs-numeral); font-weight: 500;
  color: var(--clay); line-height: 1; letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.toc-range small {
  display: block; font-family: var(--font-body); font-size: .6875rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft); margin-top: var(--s-2);
}
@media (max-width: 620px) {
  .toc-range { font-size: 1.75rem; display: flex; align-items: baseline; gap: var(--s-3); }
  .toc-range small { margin-top: 0; }
}
.toc h3 { margin-bottom: var(--s-2); }
.toc p { font-size: var(--fs-small); color: var(--ink-soft); }

/* learning channels */
/* A sub-section break inside a section: a step down from --section-y, and it
   scales, so on a phone it is 44px rather than a flat 72. */
.channels { margin-top: clamp(44px, 4.6vw, 72px); }
/* The bar and the three cards below it were on different grids: segments at
   38/34/28 against three equal card columns, so no label stood over the card it
   names. The three segments are equal now, which puts each label's centre on
   its card's centre (equal thirds of a continuous bar and equal columns with
   equal gaps share the same three centres). The bar reads as the key to the
   three cards, which is what it is. */
.balance {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-5); height: 52px; margin-bottom: var(--s-5);
}
.balance div {
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-md); box-shadow: inset 0 0 0 1px rgba(12, 26, 47, .1);
  font-size: .8125rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink); text-align: center; padding-inline: 4px; min-width: 0;
}
.balance .b1 { background: var(--green); }
.balance .b2 { background: #c9d766; }
.balance .b3 { background: var(--green-tint); }
/* "Observing / Listening / Performing" are longer than the words they replaced;
   the narrowest segment has to hold ten characters at 390px. */
@media (max-width: 620px) {
  .balance { height: 46px; gap: var(--s-3); }
  .balance div { font-size: .625rem; letter-spacing: .02em; }
}

.channel-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); margin-top: 0; }
@media (max-width: 780px) { .channel-grid { grid-template-columns: minmax(0, 1fr); } }
.channel {
  background: var(--card); border: 1px solid var(--rule-soft);
  border-radius: var(--r-md); padding: var(--s-5);
}
.channel h4 { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-2); color: var(--blue); }
.channel h4 svg { color: var(--green-deep); flex: none; }
.channel p { font-size: var(--fs-small); color: var(--ink-soft); }

/* eight intelligences */
.mi { margin-top: clamp(44px, 4.6vw, 72px); }
.mi-grid {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4);
  margin-top: var(--s-6);
}
@media (max-width: 1040px) { .mi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .mi-grid { grid-template-columns: minmax(0, 1fr); } }
.mi-card {
  background: var(--card); border: 1px solid var(--rule-soft); border-radius: var(--r-md);
  padding: var(--s-5); display: flex; flex-direction: column;
  border-top: 3px solid var(--green);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.mi-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.mi-card .icon { color: var(--blue); margin-bottom: var(--s-3); }
.mi-card h4 { font-size: 1rem; margin-bottom: var(--s-3); color: var(--ink); }
.mi-card dl { margin: 0; display: grid; gap: var(--s-3); }
.mi-card dt {
  font-size: .6875rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: 2px;
}
.mi-card dd { margin: 0; font-size: .875rem; line-height: 1.5; }
.mi-card .strengthen dd { color: var(--clay); font-weight: 500; }

/* --------------------------------------------------------------------------
   11 · Enquiry + map
   -------------------------------------------------------------------------- */
.enquire-grid {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(26px, 3.2vw, 48px); align-items: start;
}
@media (max-width: 940px) { .enquire-grid { grid-template-columns: minmax(0, 1fr); } }

.form-card {
  background: var(--card); border: 1px solid var(--rule); border-radius: var(--r-lg);
  padding: clamp(22px, 3.2vw, 40px); box-shadow: var(--shadow-sm);
}
.field { margin-bottom: var(--s-5); }
.field:last-of-type { margin-bottom: 0; }
.field label, .fieldset-legend {
  display: block; font-size: .875rem; font-weight: 600; color: var(--ink);
  margin-bottom: var(--s-2);
}
.req { color: var(--danger); font-weight: 700; }
.opt { color: var(--ink-soft); font-weight: 400; }
.hint { display: block; font-size: .8125rem; color: var(--ink-soft); font-weight: 400; margin-top: 2px; }

.field input[type="text"], .field input[type="tel"], .field input[type="email"],
.field select, .field textarea {
  width: 100%; min-height: 50px; padding: var(--s-3) var(--s-4);
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  background: var(--paper); border: 1.5px solid var(--rule); border-radius: var(--r-sm);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.field textarea { min-height: 118px; resize: vertical; line-height: 1.55; }
.field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none'%3E%3Cpath d='M1 1l6 6 6-6' stroke='%234a5567' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s-4) center;
  padding-right: var(--s-8);
}
.field input:hover, .field select:hover, .field textarea:hover { border-color: #c6b79b; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--blue); background: #fff;
  box-shadow: 0 0 0 3px rgba(0, 61, 145, .16);
}
/* Keyboard focus must stay unmistakable: the soft inner glow above is not a
   sufficient indicator on its own, least of all on a field already flagged red. */
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
  outline: 3px solid var(--focus); outline-offset: 2px;
}

.field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); }
@media (max-width: 560px) { .field-row { grid-template-columns: minmax(0, 1fr); gap: 0; } }
.field-row .field { margin-bottom: var(--s-5); }

.error {
  display: none; margin-top: var(--s-2); font-size: .8125rem; font-weight: 500;
  color: var(--danger); align-items: flex-start; gap: 6px; line-height: 1.45;
}
.error svg { flex: none; margin-top: .3em; }
.field.is-invalid .error { display: flex; }
.field.is-invalid input, .field.is-invalid select, .field.is-invalid textarea {
  border-color: var(--danger); background: #fdf5f4;
}

.consent {
  display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: var(--s-3);
  align-items: start; padding: var(--s-4); background: var(--paper);
  border: 1.5px solid var(--rule); border-radius: var(--r-sm);
}
.consent.is-invalid { border-color: var(--danger); background: #fdf5f4; }
.consent input[type="checkbox"] {
  width: 24px; height: 24px; margin: 1px 0 0; accent-color: var(--blue); cursor: pointer;
}
.consent label { font-size: .875rem; font-weight: 400; line-height: 1.5; margin: 0; cursor: pointer; }

/* The `.wiring-note` caution that sat above the send button is removed at the
   owner's instruction: submission is being wired up with the domain
   deployment. The form's behaviour is unchanged, and the success panel still
   says plainly where the message is going. */
.form-submit { margin-top: var(--s-5); }

.form-success {
  display: none; text-align: left;
  background: var(--card); border: 1px solid var(--green); border-top: 4px solid var(--green);
  border-radius: var(--r-lg); padding: clamp(24px, 3.2vw, 40px); box-shadow: var(--shadow-sm);
}
.form-success.is-visible { display: block; }
.form-success .tick {
  width: 52px; height: 52px; border-radius: 50%; background: var(--green-tint);
  display: flex; align-items: center; justify-content: center; color: var(--green-deep);
  margin-bottom: var(--s-4);
}
.form-success h3 { margin-bottom: var(--s-3); }
.form-success p { font-size: var(--fs-small); color: var(--ink-soft); }
.form-success .actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-5); }
@media (max-width: 560px) { .form-success .actions .btn { width: 100%; } }
.form-success .restart {
  background: none; border: 0; padding: 4px 0; margin-top: var(--s-5);
  font-family: var(--font-body); font-size: .875rem; color: var(--blue);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}

.contact-card {
  background: var(--blue-deep); color: #fff; border-radius: var(--r-lg);
  padding: clamp(22px, 3vw, 34px); margin-bottom: var(--s-5);
}
.contact-card h3 { color: #fff; font-size: 1.4rem; margin-bottom: var(--s-4); }
.contact-list { list-style: none; display: grid; gap: var(--s-4); }
.contact-list li { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: var(--s-3); align-items: start; }
.contact-list svg { color: var(--green); margin-top: 3px; }
.contact-list span { display: block; font-size: .75rem; letter-spacing: .12em; text-transform: uppercase; color: #9fb0c9; margin-bottom: 2px; }
.contact-list a, .contact-list p { color: #fff; font-size: var(--fs-small); text-decoration: none; margin: 0; line-height: 1.5; }
/* WCAG 2.2 §2.5.8: standalone links need a 24×24 target, not 19px of text. */
.contact-list a { display: inline-block; padding-block: 3px; }
.contact-list a:hover { color: var(--green); text-decoration: underline; }
.contact-card .btn { margin-top: var(--s-5); }

.map-frame {
  border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--rule);
  background: var(--paper-2); aspect-ratio: 16 / 11;
}
.map-frame iframe { width: 100%; height: 100%; border: 0; display: block; filter: saturate(.9); }
.map-link {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-3); padding-block: 4px;
  font-size: .875rem; font-weight: 500; color: var(--blue);
  text-underline-offset: 3px;
}
.map-link svg { flex: none; color: var(--green-deep); }
.map-link:hover { color: var(--clay); }
.map-link:hover svg { color: var(--clay); }

/* --------------------------------------------------------------------------
   12 · FAQ
   -------------------------------------------------------------------------- */
.faq-grid { display: grid; grid-template-columns: minmax(0, .6fr) minmax(0, 1.4fr); gap: clamp(24px, 3vw, 48px); align-items: start; }
@media (max-width: 900px) { .faq-grid { grid-template-columns: minmax(0, 1fr); } }

.faq-list { border-top: 1px solid var(--rule); }
.faq-item { border-bottom: 1px solid var(--rule); }
.faq-item summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-5) 0; cursor: pointer; list-style: none;
  font-family: var(--font-display); font-size: clamp(1.1rem, 1rem + .5vw, 1.35rem);
  font-weight: 600; color: var(--ink); line-height: 1.3; letter-spacing: -.01em;
  transition: color var(--dur) var(--ease);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: var(--blue); }
.faq-item summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.faq-icon {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  border: 1.5px solid var(--rule); display: grid; place-items: center;
  color: var(--blue); transition: transform 260ms var(--ease), background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.faq-item[open] .faq-icon { transform: rotate(45deg); background: var(--green); border-color: var(--green); color: var(--ink); }
.faq-answer { padding: 0 0 var(--s-5); max-width: 66ch; }
.faq-answer p { font-size: var(--fs-small); color: var(--ink-soft); }
.faq-item[open] .faq-answer { animation: faqIn 280ms var(--ease) both; }
@keyframes faqIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: none; } }

.faq-aside {
  background: var(--paper-2); border-radius: var(--r-lg); padding: clamp(22px, 3vw, 32px);
  border: 1px solid var(--rule-soft);
}
.faq-aside h3 { font-size: 1.35rem; margin-bottom: var(--s-3); }
.faq-aside p { font-size: var(--fs-small); color: var(--ink-soft); }
.faq-aside .btn { margin-top: var(--s-4); }

/* --------------------------------------------------------------------------
   13 · Footer
   -------------------------------------------------------------------------- */
.site-footer { background: var(--blue-deep); color: #c8d3e5; padding-block: var(--s-9) var(--s-6); }
.footer-top {
  display: grid; grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: var(--s-7); padding-bottom: var(--s-7); border-bottom: 1px solid rgba(255, 255, 255, .16);
}
@media (max-width: 900px) { .footer-top { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-6); } }
@media (max-width: 560px) { .footer-top { grid-template-columns: minmax(0, 1fr); } }
/* Reversed lockup. The wordmark is logo blue, which is invisible on navy, and the
   mark must not be recoloured, so the logo is given its own paper panel with
   proper clear space rather than being knocked out or tinted. */
.footer-brand img {
  height: 44px; width: auto; background: var(--paper);
  padding: 18px 24px; border-radius: 12px; box-sizing: content-box;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .1);
}
@media (max-width: 560px) { .footer-brand img { height: 38px; padding: 14px 18px; } }
.footer-brand p { margin-top: var(--s-4); font-size: var(--fs-small); color: #b7c4d9; max-width: 34ch; }
.footer-col h4 { color: #fff; font-size: .75rem; letter-spacing: .14em; text-transform: uppercase; margin-bottom: var(--s-4); }
.footer-col ul { list-style: none; display: grid; gap: var(--s-3); }
.footer-col a, .footer-col p { color: #c8d3e5; font-size: .9375rem; text-decoration: none; margin: 0; line-height: 1.5; }
.footer-col a { display: inline-block; padding-block: 4px; }
.footer-col a:hover { color: var(--green); text-decoration: underline; }

/* Slim footer: logo, contact, social. No site index, no explanatory copy.
   The disclaimer and the photograph disclosure below it both stay. */
.site-footer--slim { padding-block: var(--s-7) var(--s-5); }
.footer-slim {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--s-5) var(--s-7); align-items: center;
  padding-bottom: var(--s-5); border-bottom: 1px solid rgba(255, 255, 255, .16);
}
@media (max-width: 860px) {
  .footer-slim { grid-template-columns: minmax(0, 1fr); justify-items: start; gap: var(--s-5); }
}
.footer-contact { list-style: none; display: grid; gap: var(--s-2); }
.footer-contact li { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: var(--s-3); align-items: start; }
.footer-contact svg { color: var(--green); margin-top: 5px; }
.footer-contact a {
  display: inline-block; padding-block: 3px; color: #c8d3e5;
  font-size: .9375rem; line-height: 1.5; text-decoration: none;
}
.footer-contact a:hover { color: var(--green); text-decoration: underline; }

.footer-social { list-style: none; display: flex; gap: var(--s-3); }
.footer-social a {
  display: grid; place-items: center; width: 44px; height: 44px;
  border-radius: 50%; color: var(--green);
  background: rgba(255, 255, 255, .07); border: 1px solid rgba(154, 175, 0, .4);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.footer-social a:hover { background: var(--green); color: var(--blue-deep); border-color: var(--green); }
.site-footer--slim .disclaimer { margin-top: var(--s-5); padding-top: 0; }

.disclaimer {
  margin-top: var(--s-6); padding: var(--s-5) 0 0;
  font-size: .8125rem; line-height: 1.6; color: #93a3bd; max-width: 78ch;
}
/* The photographs disclosure used to sit on the bottom row opposite the
   copyright, which left a wide gap between two unrelated lines. The two
   statements that belong together, the medical one and the photographic one,
   now share a row; the copyright keeps the row below to itself.
   Both sentences are required and neither is reworded. */
.footer-notes {
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: var(--s-4) var(--s-7); margin-top: var(--s-5);
}
@media (max-width: 760px) { .footer-notes { grid-template-columns: minmax(0, 1fr); } }
.footer-notes .disclaimer { margin-top: 0; padding-top: 0; max-width: 62ch; }
.photo-note {
  margin: 0; max-width: 46ch;
  font-size: .8125rem; line-height: 1.6; color: #93a3bd;
}
.footer-legal {
  margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid rgba(255, 255, 255, .12);
  display: flex; flex-wrap: wrap; gap: var(--s-4); justify-content: space-between;
  font-size: .8125rem; color: #93a3bd;
}
.footer-legal p { margin: 0; }
/* One site link, added in round 4 for the counsellor page. It sits opposite the
   copyright in a row that was already `space-between` with a single item in it.
   padding-block takes the target past the 24px minimum. */
.footer-legal a {
  display: inline-block; padding-block: 4px; color: #c8d3e5;
  text-decoration: none; font-weight: 500;
}
.footer-legal a:hover { color: var(--green); text-decoration: underline; }
.footer-legal a[aria-current="page"] { color: var(--green); }

/* --------------------------------------------------------------------------
   14 · Booking page

   Rebuilt on the impeccable booking page's section order and copy selection,
   in this site's own language. The fee is stated once, in type, on the same
   navy ground and behind the same drawn ridge field as the main hero; the
   list of what is in it sits beside it in a plate. Nothing further down the
   page argues with the number.
   -------------------------------------------------------------------------- */
.book-hero { padding-block: clamp(40px, 5vw, 68px) clamp(56px, 7vw, 96px); }
.book-grid {
  position: relative; display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr);
  gap: clamp(32px, 5vw, 68px); align-items: center;
}
@media (max-width: 940px) { .book-grid { grid-template-columns: minmax(0, 1fr); } }

/* The fee. One figure, set large, with its conditions as marginalia beside it
   rather than as a second sentence underneath.

   Round 4: the two meta lines were aligned on `baseline`, which puts the FIRST
   of them on the numeral's baseline and hangs the second one below it. Against
   a figure up to 84px tall that reads as the caption sagging off the bottom of
   the number, which is what was reported.

   `center` is most of the fix, but centring the two BOXES is not the same as
   centring what the eye sees, and here the two are ten pixels apart. Measured
   in Chrome at the top of the clamp: Newsreader's font box at 84px is 62 up
   and 22 down from the baseline, while the ink of "4,000" runs 61 up and stops
   at the baseline, since lining figures have no descender and the comma is too
   small to read as one. So the box hangs a fifth of its height below the digits
   and box-centring parks the caption in that empty space.

   The offset is exact and it is a constant fraction of the figure's own size:
     ascent/em - 1/2 - (cap+figure ink)/2em
     = 62/84 - .5 - (61/84)/2 = -.125em
   so the caption is lifted .125 of the figure's font-size, expressed against a
   token both rules share. It self-corrects at both ends of the clamp with no
   media query, and the equal positive margin underneath keeps the flex line the
   height it already was: the fee block does not grow by a pixel.

   Around 940-1000px, and again at phone widths, the two no longer fit on one
   line and the caption wraps under the figure. The same lift applies there and
   is still right: it takes up part of the empty descender space the figure was
   leaving under its digits. */
.fee {
  --fee-fig-size: clamp(3.4rem, 2.1rem + 5.4vw, 5.25rem);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-5);
  max-width: none;
  margin: var(--s-6) 0 var(--s-5);
  padding-top: var(--s-5); border-top: 1px solid rgba(255, 255, 255, .22);
}
.fee-fig {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--fee-fig-size); line-height: .95;
  letter-spacing: -.035em; color: var(--green);
}
.fee-meta {
  display: grid; gap: 5px;
  margin-top: calc(var(--fee-fig-size) * -.125);
  margin-bottom: calc(var(--fee-fig-size) * .125);
}
.fee-meta span {
  font-size: .75rem; font-weight: 600; letter-spacing: .13em; text-transform: uppercase;
  line-height: 1.5; color: #cfdaec;
}
.fee-meta span + span { color: var(--green); }
.book-hero .lead { color: #cdd8ea; }

/* "Inside the fee": the list of services in its own plate, the one box on this
   page the visitor is meant to read before deciding. */
.included {
  position: relative; overflow: hidden;
  /* Opaque enough that the ridge field behind the hero passes under the plate
     rather than through the list. */
  background: linear-gradient(148deg, rgba(255, 255, 255, .13) 0%,
      rgba(255, 255, 255, .06) 52%, rgba(255, 255, 255, .03) 100%),
    rgba(0, 21, 51, .82);
  border: 1px solid rgba(154, 175, 0, .45); border-radius: var(--r-lg);
  padding: clamp(22px, 2.8vw, 32px);
  box-shadow: 0 4px 12px rgba(0, 12, 34, .24), 0 22px 54px rgba(0, 12, 34, .3);
}
.included::before, .included::after {
  content: ''; position: absolute; top: 9px; width: 22px; height: 22px;
  border: 1px solid rgba(154, 175, 0, .8); border-bottom: 0; pointer-events: none;
}
.included::before { left: 9px; border-right: 0; border-radius: 5px 0 0 0; }
.included::after { right: 9px; border-left: 0; border-radius: 0 5px 0 0; }
.included .eyebrow { color: var(--green); margin-bottom: var(--s-5); }
.included .eyebrow::before { background-image: var(--mark-tilde-green); }
.included ul { list-style: none; display: grid; gap: 0; }
.included li {
  display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: var(--s-4);
  padding-block: var(--s-4); border-top: 1px solid rgba(255, 255, 255, .15);
}
.included li:first-child { border-top: 0; padding-top: 0; }
.included li > svg { color: var(--green); margin-top: 3px; }
.included b {
  display: block; font-family: var(--font-display); font-weight: 600;
  font-size: 1.0625rem; line-height: 1.3; color: #fff; letter-spacing: -.01em;
}
.included li span span { display: block; margin-top: 3px; font-size: .8125rem; line-height: 1.5; color: #b8c6dd; }
.included-foot {
  margin-top: var(--s-5); padding-top: var(--s-4);
  border-top: 1px solid rgba(255, 255, 255, .15);
  font-size: .8125rem; line-height: 1.55; color: #b8c6dd; max-width: none;
}

/* The `.covers` rules that stood here belonged to the "What the fee covers"
   section, which is gone from `book.html`; no other page used them. */

.pay-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-6); align-items: start; }
@media (max-width: 900px) { .pay-grid { grid-template-columns: minmax(0, 1fr); } }

/* The UPI card. Round 4 replaced `.pay-slot`, the dashed reserved panel with a
   disabled button in it, because there is now a real payment behind the button:
   the owner's own open-amount UPI code. A dashed placeholder beside a live
   control would have been the wrong signal in both directions, so the card is
   built as `.book-now`'s pair instead: same plate, same padding, same 3px top
   edge in the other brand colour. Neither of the two looks like the reserve. */
.pay-card {
  background: var(--card); border: 1px solid var(--rule);
  border-top: 3px solid var(--blue); border-radius: var(--r-lg);
  padding: clamp(24px, 3.4vw, 40px); box-shadow: var(--shadow-sm);
}
.pay-card > h3 { margin-bottom: var(--s-3); }
.pay-card > p { font-size: var(--fs-small); color: var(--ink-soft); }
.pay-card > .btn { margin-top: var(--s-5); }
/* `.pay-card > p` above outranks a lone class, so the footnote has to be scoped
   the same way or it inherits the 15px body size of the paragraph over it. */
.pay-card > .pay-card-foot {
  margin: var(--s-4) 0 0; font-size: .8125rem; line-height: 1.55; color: var(--ink-soft);
}

/* The left column of `.pay-grid`: the payment card with the parent/guardian note
   closing it. 24px inside the column against the grid's own 32px between them,
   so the pair reads as one column and not as three equal blocks in an L. */
.pay-col { display: grid; gap: var(--s-5); align-content: start; }

/* The note, in its new home. Three things stop it reading as a stranded bar in a
   gap, and they are all alignment rather than ornament:
   - the same 18px corner as `.pay-card` above it, not the generic 10px;
   - the same horizontal padding expression as `.pay-card`, so the icon's left
     edge lands exactly on the card's heading, and the block's right edge on its
     right edge. Two plates, one measure;
   - a hairline in the tint's own hue, which is what `.pay-card` has in --rule
     and what makes both of them read as plates rather than as washes.
   No top accent bar: the two route cards each carry one, in blue and in green,
   and a third would make this look like a third route. It is a footnote to both
   of them, so it is quieter than both of them. */
.band-foot.pay-note {
  margin-top: 0;
  padding: var(--s-5) clamp(24px, 3.4vw, 40px);
  border-radius: var(--r-lg);
  border: 1px solid rgba(154, 175, 0, .38);
}

/* --------------------------------------------------------------------------
   14a · The UPI panel

   A native <dialog>. Everything that makes a modal correct, the top layer,
   focus containment, Escape, inertness of the page behind it, is the
   platform's here rather than three hundred lines of ours.

   Two routes, one payment. The code leads on a pointer device because that is
   the device that can be scanned FROM; the tap leads on a phone because a
   phone cannot scan its own screen. Nothing is reordered to do that, so the
   reading order and the tab order are the same order everywhere. */
.pay-dialog {
  width: min(660px, calc(100% - 32px));
  max-width: none; max-height: min(88svh, calc(100% - 32px));
  margin: auto; padding: 0; overflow: auto;
  border: 0; border-radius: var(--r-lg);
  background: var(--paper); color: var(--ink);
  box-shadow: 0 6px 18px rgba(12, 26, 47, .18), 0 40px 90px rgba(12, 26, 47, .3);
}
.pay-dialog::backdrop { background: rgba(0, 20, 48, .58); }

/* One authored moment on this page: the panel lands rather than appears. Both
   halves end on the resting state, so with motion switched off, where the
   global rule cuts every duration to .001ms, the panel is simply there. */
@media (prefers-reduced-motion: no-preference) {
  .pay-dialog[open] { animation: payIn 260ms var(--ease) both; }
  .pay-dialog[open]::backdrop { animation: payFade 260ms var(--ease) both; }
}
@keyframes payIn {
  from { opacity: 0; transform: translateY(14px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes payFade { from { opacity: 0; } to { opacity: 1; } }

.pay-panel { position: relative; padding: clamp(24px, 3.2vw, 36px); }
.pay-panel:focus { outline: none; }
.pay-panel h2 { font-size: clamp(1.6rem, 1.3rem + 1.2vw, 2.1rem); padding-right: 44px; }
.pay-lede {
  margin: var(--s-3) 0 var(--s-5); max-width: 48ch;
  font-size: var(--fs-small); line-height: 1.6; color: var(--ink-soft);
}

.pay-close {
  position: absolute; top: clamp(14px, 2vw, 20px); right: clamp(14px, 2vw, 20px);
  display: grid; place-items: center; width: 40px; height: 40px;
  padding: 0; border: 1px solid var(--rule); border-radius: 50%;
  background: var(--card); color: var(--ink-soft); cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.pay-close:hover { background: var(--blue); border-color: var(--blue); color: #fff; }

/* Who the money goes to, stated as a fact to check rather than as a warning.
   It is the one thing that makes a UPI transfer verifiable by the person
   making it, so it is set as the strongest line in the panel after the title. */
.pay-payee {
  display: grid; gap: 3px; margin: 0; max-width: none;
  padding: var(--s-4) var(--s-5);
  background: var(--paper-2); border-radius: var(--r-md);
}
.pay-payee-label {
  font-size: .6875rem; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-soft);
}
.pay-payee b {
  font-family: var(--font-display); font-size: 1.375rem; font-weight: 600;
  letter-spacing: .01em; line-height: 1.2; color: var(--blue);
}
.pay-payee-check { font-size: .8125rem; line-height: 1.5; color: var(--ink-soft); }

/* One route, not two. The tap-to-pay column was removed in rev-07 and this
   went from a two-column grid to a single centred one. Kept as a grid rather
   than collapsed into the panel so that a second route can be restored without
   rebuilding the block, which is likely: Razorpay is intended to take this
   slot. */
.pay-routes {
  display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center;
  gap: clamp(20px, 3vw, 32px);
  margin-top: var(--s-5); padding-top: var(--s-5);
  border-top: 1px solid var(--rule);
}
/* The code is a printed object, so it gets a white ground and a real edge. Its
   own quiet zone is inside the file; the padding here is the paper around it.
   The SVG is the owner's payment instrument and is referenced, never touched.
   The plate is sized on the image rather than on the figure, so the caption is
   free to be wider than the code and never has to break inside a 140px box. */
.pay-qr { margin: 0; display: grid; justify-items: center; width: max-content; }
.pay-qr img {
  width: clamp(140px, 17vw, 196px); height: auto;
  padding: 10px; background: #fff;
  border: 1px solid var(--rule); border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
}
/* Sentence case, not the tracked micro-cap used for labels elsewhere: this is
   an instruction, and tracked caps do not fit the plate on one line. */
.pay-qr figcaption {
  margin-top: 8px; text-align: center;
  font-size: .75rem; line-height: 1.4; color: var(--ink-soft);
}
/* The one line about what paying does and does not do. A sentence, on the
   page's own ground, sharing a rule with the WhatsApp button that acts on it.
   Not a caution panel: those were removed last round and stay removed. */
.pay-after {
  margin-top: var(--s-5); padding-top: var(--s-5); border-top: 1px solid var(--rule);
}
.pay-after p { font-size: var(--fs-small); line-height: 1.6; color: var(--ink); max-width: none; }
.pay-after .btn { margin-top: var(--s-4); }

/* Escape and the backdrop both close the panel, but neither is visible. The
   round control at the top is the discoverable one; this is the one a thumb
   reaches at the end of a phone-height panel. */
.pay-dismiss { margin-top: var(--s-5); width: 100%; }

/* On a phone the panel is the whole screen, so vertical room is the scarce
   resource: the payee plate loses some padding and the spacing around the code
   tightens so the WhatsApp step below it stays reachable without scrolling. */
@media (max-width: 620px) {
  .pay-routes {
    gap: var(--s-4); margin-top: var(--s-4); padding-top: var(--s-4);
  }
  .pay-payee { padding: var(--s-3) var(--s-4); }
  .pay-after { margin-top: var(--s-4); padding-top: var(--s-4); }
}

/* No <dialog> support, or no scripting at all: the panel is not hidden behind a
   button that cannot open it. The dialog lives inside the card, so switching it
   to an ordinary block puts it exactly where the button was. The code stays
   reachable in either case, and nothing on this page is ever a dead control. */
.js-off #pay-open, .no-dialog #pay-open,
.js-off .pay-close, .no-dialog .pay-close,
.js-off .pay-card-foot, .no-dialog .pay-card-foot,
.js-off .pay-dismiss, .no-dialog .pay-dismiss { display: none; }
.js-off .pay-dialog, .no-dialog .pay-dialog {
  display: block; position: static; inset: auto;
  width: auto; max-width: none; max-height: none;
  margin: var(--s-5) 0 0; box-shadow: none; border-radius: 0;
  background: transparent; color: inherit; overflow: visible;
}
.js-off .pay-panel, .no-dialog .pay-panel {
  padding: var(--s-5) 0 0; border-top: 1px solid var(--rule);
}
.js-off .pay-panel h2, .no-dialog .pay-panel h2 { font-size: var(--fs-h3); padding-right: 0; }

.book-now {
  background: var(--card); border: 1px solid var(--rule); border-top: 3px solid var(--green);
  border-radius: var(--r-lg);
  padding: clamp(24px, 3.4vw, 40px); box-shadow: var(--shadow-sm);
}
.book-now h3 { margin-bottom: var(--s-3); }
.book-now > p { font-size: var(--fs-small); color: var(--ink-soft); }
.book-now .btn--wa { margin-top: var(--s-5); }

/* The three routes that actually exist, as a list. Three more cards here would
   have made the reserved payment panel look like a fourth option. */
.routes { list-style: none; margin-top: var(--s-5); display: grid; gap: 0; }
.routes li { border-top: 1px solid var(--rule); }
.routes a, .routes .routes-static {
  display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: var(--s-3);
  align-items: start; padding-block: var(--s-4); text-decoration: none;
}
.routes a { transition: color var(--dur) var(--ease); }
.routes svg { color: var(--green-deep); margin-top: 4px; }
.routes a:hover svg, .routes a:hover b { color: var(--clay); }
.routes-label {
  display: block; font-size: .6875rem; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-soft); margin-bottom: 2px;
}
.routes b {
  display: block; font-size: 1rem; font-weight: 600; color: var(--blue);
  transition: color var(--dur) var(--ease);
}
.routes .routes-static b { color: var(--ink); }
.routes-sub { display: block; margin-top: 3px; font-size: .8125rem; line-height: 1.5; color: var(--ink-soft); }

.book-faq { max-width: 820px; }

.backlink {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: .875rem; font-weight: 500; color: #dfe7f3;
  text-decoration: none; margin-bottom: var(--s-6); padding-block: 4px;
}
.backlink svg { color: var(--green); }
.backlink:hover { color: var(--green); text-decoration: underline; }

/* --------------------------------------------------------------------------
   14b · Counsellor page (counsellor.html)

   Built entirely from grammar that already exists: the booking page's hero
   shape and backlink, the `.analyst-id` signature block from the "Who sits
   across the table" band, `.step` cards and `.band-list`. Only three things
   here are new, and all three are page-scoped:
   the portrait's own aspect ratio, a width cap on it, and a prose/aside grid.
   -------------------------------------------------------------------------- */
/* The source is 1122x1195. The frame carries that ratio exactly, so nothing is
   cropped and the compliance crop that removed the desk nameplate is not
   quietly undone by an `object-fit` decision here.
   Column width at 1440 is ~468 CSS px against 1122 source pixels: 2.4x, which
   holds on a 2x phone and is the largest honest use of the file. */
.counsellor-hero .hero-figure .frame { aspect-ratio: 1122 / 1195; }
/* Her photograph is graded like the one in the index band, not like the stock
   photography: warm sepia is the treatment that makes nine unrelated sources
   read as one commission, and this is a real portrait of a real person. */
.counsellor-hero .hero-figure .frame > img {
  object-position: 50% 50%; filter: saturate(.9) contrast(1.02);
}
/* Below 900px the hero collapses to one column and the figure would otherwise
   stretch to the full shell. At 860 CSS px on a 3x phone the file would be
   asked for 2580 device pixels and it has 1122. Capped, it never runs past
   about 3.2x at 390 and 2.6x at 900. */
@media (max-width: 900px) {
  .counsellor-hero .hero-figure { max-width: 440px; }
}
.counsellor-hero .analyst-id { margin-top: var(--s-5); }

/* Prose beside a short factual aside. Same shape as `.report-grid`, at the
   ratio the body measure wants: the left column holds 62ch of Lexend at the
   shell's 1120px and the card takes what is left. */
.bio-grid {
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(24px, 3vw, 48px); align-items: start;
}
@media (max-width: 860px) { .bio-grid { grid-template-columns: minmax(0, 1fr); } }
/* The aside lists facts, not steps, so it does not lift under the pointer the
   way the three process cards do. */
.bio-grid .step:hover { transform: none; box-shadow: var(--shadow-sm); }

/* Round 4. The owner: "there is font inconsistency where subheading is grey and
   slightly big and body is black and smaller, choose one and keep in throughout
   (My pick is gray)."
   One ink for all prose on this page, and it is the grey they picked:
   `--ink-soft` #4A5567, 7.0:1 on `--paper`, comfortably past AA at body size and
   the same colour `.lead`, `.step p` and the whole secondary layer of the site
   already use. Nothing new is invented and nothing drops below AA.
   Size is left alone on purpose. The complaint is about two different
   treatments, and colour is the half of it the owner named; running three
   paragraphs of body copy at `--fs-lead` (up to 21.6px) would shout at a reader
   and cut the measure, and `.lead` is a shared section-head component, so
   shrinking it here would make this page's section head disagree with the other
   two. Grey throughout, with size left to mark the opening line. Scoped to
   `.bio-grid`, which exists on this page only.
   This is also the site's own default rather than an exception to it. Almost all
   prose here is already `--ink-soft`: `.band > p`, `.step p`, `.faq-answer p`,
   `.claims p`, `.toc p`, `.channel p`. The bio prose was the outlier only
   because it is a bare `<p>` with no component class on it, so it fell through
   to the `body` colour. `.band-list li` inside the aside stays at `--ink`,
   exactly as the identical lists on index.html do. */
.bio-grid > div > p { color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   15 · Ridge fields

   Contour drawings computed in main.js from an arch, a loop core or a whorl,
   the three dermatoglyphic pattern classes. Same family as the logo arch and
   the two ridge-field SVGs, but generated, so a field fills a whole band at
   any size without tiling and can draw itself on.

   Cost is deliberately bounded. The geometry is computed once, off the
   critical path; the draw-on runs once; the only continuous motion is two
   composited layers drifting about 1% of their box against each other, on
   transform and opacity alone, paused the moment the band leaves the screen
   and switched off entirely under reduced motion.
   -------------------------------------------------------------------------- */
.ridge-field {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden; pointer-events: none; contain: layout paint;
  opacity: 0; transition: opacity 500ms var(--ease);
}
.ridge-field.is-ready { opacity: 1; }
.ridge-ink, .ridge-glow { position: absolute; inset: -6%; display: block; }

/* The resting state IS the drift's `from` frame, and that is load-bearing.
   `is-live` is added on a timer 1.6s after the draw, so whatever gap exists
   between where a layer rests and where its keyframe starts is paid off in one
   frame. Resting at `transform: none` cost the ink 14px and the glow 80px
   sideways, and dropped the glow from opacity 1 to .72, all inside that single
   frame: the jump the field was reported for. Matching the two states keeps the
   drift's designed midpoint and its full excursion and removes the step.
   Under reduced motion the drift never runs, so both layers are put back on
   their composed position below. */
.ridge-ink  { transform: translate3d(-.9%, .55%, 0) scale(1); }
.ridge-glow { transform: translate3d(5%, 3.5%, 0); opacity: .72; }
.ridge-ink svg { width: 100%; height: 100%; display: block; }
.ridge-ink path { fill: none; vector-effect: non-scaling-stroke; }
.ridge-ink .r-minor { stroke-width: 1; }
.ridge-ink .r-key   { stroke-width: 2; }

/* Draw-on, the same idea as the logo mark. --len is only set when the field is
   going to animate, so with motion off, or with no script at all, every line is
   simply already drawn.

   The window this runs in is the field's whole running cost. stroke-dashoffset
   is not a composited property, so every frame of the draw repaints the field,
   and the bill is set by how many frames that is, not by how many lines move in
   them. 1000ms of draw behind a 500ms cascade is the shortest window the
   outward-in reading survives at; it is a third cheaper than the 2400ms it
   replaced and, on a phone, brisker is also better. */
.ridge-ink path { stroke-dashoffset: var(--len, 0); }
.ridge-field.is-drawn .ridge-ink path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1000ms var(--ease);
  transition-delay: var(--pd, 0ms);
}

@media (prefers-reduced-motion: no-preference) {
  .ridge-field.is-live .ridge-ink  { animation: ridgeDrift 38s var(--ease) infinite alternate; }
  .ridge-field.is-live .ridge-glow { animation: glowDrift 29s var(--ease) infinite alternate; }
  .ridge-field.is-paused .ridge-ink,
  .ridge-field.is-paused .ridge-glow { animation-play-state: paused; }
}
@keyframes ridgeDrift {
  from { transform: translate3d(-.9%, .55%, 0) scale(1); }
  to   { transform: translate3d(.9%, -.6%, 0) scale(1.022); }
}
@keyframes glowDrift {
  from { transform: translate3d(5%, 3.5%, 0); opacity: .72; }
  to   { transform: translate3d(-5%, -3.5%, 0); opacity: 1; }
}

/* Hero colourway: chalk minor lines with the logo green on every fourth
   contour, and a slow green core light drifting under them. */
.hero .ridge-field .r-minor { stroke: rgba(255, 255, 255, .19); }
.hero .ridge-field .r-key   { stroke: rgba(154, 175, 0, .44); }
/* the fact strip sits on the bottom edge, so the lines let go before it */
.hero .ridge-ink {
  -webkit-mask-image: linear-gradient(to top, transparent 1%, #000 17%);
  mask-image: linear-gradient(to top, transparent 1%, #000 17%);
}
.hero .ridge-glow {
  background: radial-gradient(44% 52% at 66% 44%, rgba(154, 175, 0, .17), rgba(154, 175, 0, 0) 70%);
}
/* Behind the copy column the lines have to give way to the h1. */
.hero .ridge-field {
  -webkit-mask-image: linear-gradient(100deg, transparent 11%, rgba(0, 0, 0, .34) 38%, #000 66%);
  mask-image: linear-gradient(100deg, transparent 11%, rgba(0, 0, 0, .34) 38%, #000 66%);
}
@media (max-width: 900px) {
  .hero .ridge-field {
    -webkit-mask-image: linear-gradient(178deg, transparent 3%, rgba(0, 0, 0, .38) 26%, #000 54%);
    mask-image: linear-gradient(178deg, transparent 3%, rgba(0, 0, 0, .38) 26%, #000 54%);
  }
}

/* --------------------------------------------------------------------------
   16 · Motion: scroll reveal
   -------------------------------------------------------------------------- */
/* 10px, not 18px: over 300ms a longer rise reads as the element travelling to
   its place rather than settling onto it, and travel is what made the reveal
   feel like a late arrival.

   The authored per-element staggers are halved at the one place they are
   applied instead of being retyped in twenty inline styles. The rhythm the
   staggers describe is worth keeping; at the old scale the last item in a
   180ms group did not start until the first had almost finished. */
[data-reveal] {
  opacity: 0; transform: translateY(10px);
  transition: opacity var(--dur-reveal) var(--ease), transform var(--dur-reveal) var(--ease);
  transition-delay: calc(var(--reveal-delay, 0ms) * .5);
}
[data-reveal].is-in { opacity: 1; transform: none; }

.js-off [data-reveal] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  .band:hover .band-media img,
  .step:hover, .mi-card:hover { transform: none !important; }
  /* No drift here, so the two ridge layers sit on the position they were
     composed at rather than at the first frame of an animation that will
     never run. */
  .ridge-ink  { transform: none; }
  .ridge-glow { transform: none; opacity: 1; }
}

/* print ------------------------------------------------------------------ */
@media print {
  .site-header, .nav-scrim, .map-frame, .hero::before, .ridge-field { display: none !important; }
  body { background: #fff; }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}
