/*
 * Bayesrock — global foundation and the single semantic token contract.
 *
 * WHAT IS SHARED WITH numtrade.in, AND WHAT IS NOT.
 *
 * Shared, on purpose: the token contract. Token NAMES, the spacing scale, the
 * type scale and the breakpoints are identical to site.css in the numtrade.in
 * repository, so a developer moving between the two is working in one system
 * and neither site can drift on rhythm or measure.
 *
 * Not shared, on purpose: the look. NumTrade is a technology company and its
 * furniture is bordered cards with an offset shadow on pure white. Bayesrock
 * is an investment company: warm paper stock, and furniture made of hairlines
 * and alignment — ruled rows and a drawn group tree, with no fills or shadows
 * anywhere. A portfolio reads as a printed record, not as a set of tiles.
 *
 * Section 1 is the shared contract. Section 2 is the Bayesrock theme. Adding a
 * page should not require new CSS: compose partials/components/.
 */


/* ==========================================================================
   1. TOKEN CONTRACT
   ========================================================================== */

:root {
  color-scheme: light;

  /*
   * Colour. Semantic roles, not literal shades.
   *
   * The stock is a warm off-white — printed prospectus paper, not screen
   * white — and the ink is warm near-black to match. That is the one place
   * Bayesrock departs from numtrade's pure #ffffff / #111111: same family,
   * different paper, so the two sites are siblings rather than copies.
   */
  --color-ink: #14140f;
  --color-ink-soft: #3d3c35;
  --color-paper: #f5f4ef;
  --color-muted: #5d5c54;
  --color-line: #d8d6cd;

  /* Typeface roles. */
  --font-sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-ui: Arial, Helvetica, sans-serif;
  --font-mono: "DejaVu Sans Mono", "Liberation Mono", "Lucida Console", monospace;

  /*
   * Type scale, smallest to largest. Every step is fluid: it interpolates
   * between a phone and a large desktop, so no step needs a per-device
   * override and the ratios between steps hold at every width.
   *
   * The comment on each line is the resolved range, min -> max.
   *
   * Body never drops below 1rem. Shrinking body copy on a phone is the wrong
   * trade — the screen is smaller, not the reader — and sub-16px text also
   * triggers zoom-on-focus in iOS Safari.
   */
  --size-legal: clamp(0.65rem, 0.62rem + 0.14vw, 0.6875rem);      /* 10.4 -> 11 */
  --size-label: clamp(0.6875rem, 0.665rem + 0.1vw, 0.75rem);      /*   11 -> 12 */
  --size-small: clamp(0.875rem, 0.85rem + 0.13vw, 0.9375rem);     /*   14 -> 15 */
  --size-body: clamp(1rem, 0.97rem + 0.14vw, 1.0625rem);          /*   16 -> 17 */
  --size-lede: clamp(1.0625rem, 1.7vw, 1.25rem);                  /*   17 -> 20 */
  --size-brand: clamp(1.0625rem, 1rem + 0.28vw, 1.25rem);         /*   17 -> 20 */
  --size-heading: clamp(1.75rem, 3vw, 2.5rem);                    /*   28 -> 40 */

  /* Line heights. */
  --line-heading: 1.04;
  --line-snug: 1.4;
  --line-body: 1.6;

  /* Letter spacing. Positive for uppercase runs, negative for large display. */
  --tracking-label: 0.06em;
  --tracking-wide: 0.12em;
  --tracking-tight: -0.035em;

  /* Layout. */
  --content-width: 72rem;
  --text-measure: 42rem;
  --page-gutter: 1.25rem;
  --control-min-height: 2.75rem;
  --border-width: 1px;

  /* Spacing scale. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-7: 2rem;
  --space-8: 3rem;
  --space-9: 4rem;
}


/* ==========================================================================
   2. BAYESROCK THEME
   ========================================================================== */

:root {
  /* A second, slightly deeper sheet for sections that sit back from the page. */
  --color-wash: #eceae2;

  /* A hairline that is awake. One step darker than --color-line and nothing
     more: enough to read as "this line is part of the path you are pointing
     at", not enough to become a second visual weight on the page. */
  --color-line-active: #a09e94;

  /* Veil behind the open mobile menu. Warm near-black at low opacity: on a
     light page a white scrim would wash the panel out instead of seating it. */
  --color-scrim: rgba(20, 20, 15, 0.18);

  --header-height: 5rem;

  --size-navigation: clamp(0.8125rem, 0.79rem + 0.1vw, 0.875rem); /*   13 -> 14 */
  --size-footer: clamp(0.8125rem, 0.79rem + 0.1vw, 0.875rem);     /*   13 -> 14 */
  --size-hero: clamp(2.3125rem, 6vw, 4.75rem);                    /*   37 -> 76 */

  /* The rail that carries index numbers. One value, so every ruled list on
     every page aligns to the same column. */
  --rail-width: 3.5rem;

  /* Motion. One curve for the whole site. */
  --ease-out: cubic-bezier(0.2, 0.65, 0.3, 1);
}


/* ==========================================================================
   3. BASE
   ========================================================================== */

* { box-sizing: border-box; }

html {
  scroll-padding-top: calc(var(--header-height) + var(--space-4));
}

body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-sans);
  font-size: var(--size-body);
  line-height: var(--line-body);
  -webkit-text-size-adjust: 100%;
}

a { color: inherit; }

a:focus-visible,
summary:focus-visible,
button:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 3px;
}

.skip-link,
.skip-link:visited {
  position: absolute;
  z-index: 30;
  top: -5rem;
  left: var(--page-gutter);
  padding: var(--space-2) var(--space-3);
  background: var(--color-ink);
  color: var(--color-paper);
  text-decoration: none;
}

.skip-link:focus { top: var(--space-4); }

.shell {
  width: min(
    calc(100% - var(--page-gutter) - var(--page-gutter)),
    var(--content-width)
  );
  margin: 0 auto;
}

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


/* ==========================================================================
   4. HEADER
   ========================================================================== */

.site-header {
  position: sticky;
  z-index: 20;
  top: 0;
  background: var(--color-paper);
  border-bottom: var(--border-width) solid var(--color-line);
}

.site-header__inner {
  position: relative;
  display: flex;
  min-height: var(--header-height);
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.brand {
  display: inline-flex;
  min-width: 0;
  min-height: var(--control-min-height);
  flex-direction: column;
  justify-content: center;
  color: var(--color-ink);
  line-height: 1.15;
  text-decoration: none;
}

.brand__name {
  font-size: var(--size-brand);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
}

.site-menu {
  display: none;
  position: relative;
  margin-left: auto;
}

.site-menu > summary { display: none; }

.site-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-5);
  font-size: var(--size-navigation);
}

.site-nav a {
  text-decoration: none;
  text-underline-offset: var(--space-1);
}

.site-nav a:hover,
.site-nav a[aria-current="page"] { text-decoration: underline; }

/*
 * Two centred bars, not a "+" character. A typed plus is positioned on the
 * font's math axis rather than the middle of its box, so it never sits level
 * with the word beside it and it rotates around the wrong point. Two bars
 * crossing at the box centre are exact at any size, in any font, and rotate
 * into a true ×.
 */
.menu-toggle-mark {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: 0.6875rem;
  height: 0.6875rem;
  transition: rotate 220ms var(--ease-out);
}

.menu-toggle-mark::before,
.menu-toggle-mark::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: currentColor;
  translate: -50% -50%;
}

.menu-toggle-mark::before {
  width: 100%;
  height: var(--border-width);
}

.menu-toggle-mark::after {
  width: var(--border-width);
  height: 100%;
}


/* ==========================================================================
   5. SECTIONS AND TYPE
   ========================================================================== */

.eyebrow {
  margin: 0 0 var(--space-4);
  color: var(--color-muted);
  font-size: var(--size-label);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.hero {
  padding: clamp(4.5rem, 9vw, 7.5rem) 0 clamp(4rem, 7vw, 6rem);
  border-bottom: var(--border-width) solid var(--color-line);
  scroll-margin-top: var(--header-height);
}

.hero h1 {
  max-width: 20ch;
  margin: 0;
  font-size: var(--size-hero);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 1;
  text-wrap: balance;
}

.hero-summary {
  max-width: var(--text-measure);
  margin: var(--space-7) 0 0;
  color: var(--color-ink-soft);
  font-size: var(--size-lede);
  text-wrap: pretty;
}

.section {
  padding: clamp(3.5rem, 6vw, 5.5rem) 0;
  scroll-margin-top: var(--header-height);
}

.section--wash {
  background: var(--color-wash);
  border-top: var(--border-width) solid var(--color-line);
  border-bottom: var(--border-width) solid var(--color-line);
}

.section-title {
  max-width: 21ch;
  margin: 0;
  font-size: var(--size-heading);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  line-height: var(--line-heading);
  text-wrap: balance;
}

.section-intro {
  max-width: var(--text-measure);
  margin: var(--space-4) 0 0;
  color: var(--color-ink-soft);
  text-wrap: pretty;
}

.split-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(var(--space-7), 7vw, 6rem);
  align-items: start;
}

.split-copy {
  margin: 0;
  color: var(--color-ink-soft);
  font-size: var(--size-lede);
  text-wrap: pretty;
}

/* An action is a ruled word, not a button. Investment sites state; they do
   not solicit clicks. */
.action {
  display: inline-flex;
  min-height: var(--control-min-height);
  align-items: center;
  margin-top: var(--space-7);
  padding-bottom: var(--space-1);
  border-bottom: 2px solid var(--color-ink);
  font-size: var(--size-navigation);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-decoration: none;
  text-transform: uppercase;
}

.action:hover { color: var(--color-ink-soft); border-bottom-color: var(--color-ink-soft); }


/* ==========================================================================
   6. RULED INDEXES
   The site's one repeating structure. Both lists share the same numbering
   rail (--rail-width) so the whole page aligns to a single vertical grid.
   ========================================================================== */

.ruled-list {
  margin: var(--space-8) 0 0;
  padding: 0;
  border-top: var(--border-width) solid var(--color-ink);
  list-style: none;
}

.ruled-list > li {
  border-bottom: var(--border-width) solid var(--color-line);
}

.rail-number {
  color: var(--color-muted);
  font-size: var(--size-label);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
}

/* 6.1 Steps — a numbered statement of how the group operates. */

.step {
  display: grid;
  grid-template-columns: var(--rail-width) minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-5) 0;
}

.step__title {
  margin: 0;
  font-size: var(--size-body);
  font-weight: 700;
}

.step__copy {
  margin: var(--space-2) 0 0;
  max-width: var(--text-measure);
  color: var(--color-muted);
  font-size: var(--size-small);
}

/* ==========================================================================
   6.2 GROUP TREE
   An org chart drawn entirely from a nested <ul>. Every connector is a
   pseudo-element border, so the diagram is real text — no SVG, no image, and
   it still reads correctly with CSS off.

   Desktop: branches spread horizontally from the root.
   Below 44rem: the same markup becomes an indented left-spine tree, because a
   horizontal chart cannot fit a phone without scrolling.
   ========================================================================== */

.tree-wrap {
  margin-top: var(--space-8);
  overflow-x: auto;
}

.tree,
.tree ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tree {
  width: 100%;
}

.tree li {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: var(--space-6);
}

/* The root has no parent, so it carries no incoming connector. */
.tree > li { padding-top: 0; }

/*
 * Child row: the stem dropping from the parent node down to the sibling bar.
 *
 * Equal columns, and no gap, are both load-bearing.
 *
 * Equal columns are what makes the chart centre exactly. Sized to content, a
 * sibling that owns a wide subtree takes a wide box and a leaf takes a narrow
 * one, so the midpoint of the row stops being the midpoint between the two
 * node centres — the stem drops in one place and the bar is centred on
 * another. With every column the same width, each node sits on its column's
 * centre, those centres are evenly spaced, and the row midpoint is the bar
 * midpoint by construction, at every depth.
 *
 * No gap, because each li draws half the bar across its own width. A gap
 * would leave the halves unable to meet and break the bar. Separation between
 * nodes comes from the node being narrower than its column instead.
 */
.tree ul {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  width: 100%;
  padding-top: var(--space-6);
}

.tree ul::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 0;
  height: var(--space-6);
  border-left: var(--border-width) solid var(--color-line);
}

/*
 * Each child draws one half of the sibling bar plus its own drop:
 *   ::before  left half of the bar
 *   ::after   right half of the bar, and the vertical drop to the node
 * Trimming the outer halves on the first and last child stops the bar
 * overhanging the row, and an only child is left with just the drop.
 */
.tree li::before,
.tree li::after {
  content: "";
  position: absolute;
  top: 0;
  right: 50%;
  width: 50%;
  height: var(--space-6);
  border-top: var(--border-width) solid var(--color-line);
  transition: border-color 200ms var(--ease-out);
}

.tree ul::before {
  transition: border-color 200ms var(--ease-out);
}

.tree li::after {
  right: auto;
  left: 50%;
  border-left: var(--border-width) solid var(--color-line);
}

.tree > li::before,
.tree > li::after { display: none; }

.tree li:first-child::before { border-top: 0; }
.tree li:last-child::after { border-top: 0; }

/*
 * Never wider than its column, so a node cannot spill into a sibling's half
 * of the bar. The subtracted space is what separates adjacent nodes: without
 * it a row whose columns are narrower than 13rem renders as one unbroken
 * strip of boxes sharing borders, because each node fills its column exactly.
 */
/*
 * position/z-index are not cosmetic here. The connectors are absolutely
 * positioned pseudo-elements of the li, so with a static node they paint
 * above it. At rest the drop stops exactly on the node's top edge and nothing
 * shows, but the hover lift slides the node up under the line and 2px of
 * connector gets drawn inside the box. Lifting the node onto its own layer
 * puts the join behind it at every position.
 */
.tree__node {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: min(11rem, calc(100% - var(--space-4)));
  padding: var(--space-3) var(--space-4);
  /* Opaque so the incoming connector tucks under the node on hover-lift
     instead of showing through it. */
  background: var(--color-paper);
  border: var(--border-width) solid var(--color-line);
  text-align: center;
  transition: border-color 200ms var(--ease-out);
}

.tree > li > .tree__node {
  border-color: var(--color-ink);
}

.tree__name {
  margin: 0;
  font-size: var(--size-small);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  line-height: 1.2;
}

/*
 * Two lines are reserved whether or not the text needs them. Sibling boxes are
 * sized by their own content, so a role that wraps ("Research programme") stood
 * taller than the one-word roles beside it and the row came out ragged. Holding
 * the height means a row of boxes is a row of boxes, whatever the labels say.
 */
.tree__meta {
  min-height: 2lh;
  margin: var(--space-2) 0 0;
  color: var(--color-muted);
  font-size: var(--size-label);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  line-height: 1.3;
  text-transform: uppercase;
}

/*
 * Hover traces the lineage of one node: the node, every ancestor above it,
 * and the connectors joining them — and nothing else. Hovering "Trade" lights
 * Trade, the line up to NumTrade, NumTrade, the line up to Bayesrock, and
 * Bayesrock. Its siblings and their connectors stay exactly as they were.
 *
 * Colour only. Nothing moves, nothing fills, nothing changes size — a chart
 * that shifts under the pointer is harder to read, not easier, and the reader
 * is here to follow a line.
 *
 * Restricted to a real pointer at the width where the chart is horizontal.
 * Below 56rem the tree is a vertical spine, where lineage is already obvious
 * from indentation, and a touch device has no hover to speak of.
 */
@media (hover: hover) and (min-width: 56.0625rem) {
    /*
     * Every class below is applied by assets/js/tree.js, which walks from the
     * hovered node up to the root.
     *
     * An earlier version selected the ancestors in CSS with
     * :has(.tree__node:hover). That reads better, but the horizontal run of
     * the sibling bar cannot be expressed that way at all — it is built from
     * half-segments belonging to each li, and the piece joining a parent's
     * stem to one child usually spans siblings that are NOT on the path. So
     * script was required regardless, and having one mechanism rather than
     * two is worth more than the tidier selector.
     */

    /* The boxes. Scoped as ".tree ul li" to exclude the root, whose border is
       already --color-ink: this rule is more specific than the one that sets
       that, so including the root would replace ink with the lighter active
       hairline and make the box fade on hover instead of firming up. The root
       needs no highlight — it is already the darkest box, and the lit stem
       arrives at it. */
    .tree ul li.is-path > .tree__node {
        border-color: var(--color-line-active);
    }

    /* The drop arriving at each node on the path. */
    .tree li.is-path::after {
        border-left-color: var(--color-line-active);
    }

    /* The stem descending into a row that the path passes through. */
    .tree ul.is-path::before {
        border-left-color: var(--color-line-active);
    }

    /* The horizontal run between that stem and the child it leads to. */
    .tree li.is-path-before::before {
        border-top-color: var(--color-line-active);
    }

    .tree li.is-path-after::after {
        border-top-color: var(--color-line-active);
    }
}

/*
 * Depth, derived from nesting rather than written into the markup.
 *
 * This was an inline style="--depth: N" on every node. Inline style
 * attributes need style-src-attr 'unsafe-inline' in the Content-Security-
 * Policy, and the deployed policy grants style-src 'self' only — the
 * attributes would be dropped, every node would fall back to --depth 0, and
 * the whole chart would reveal in one flat beat. Nesting already carries the
 * depth, so read it from there and the policy can stay strict.
 */
.tree > li > .tree__node { --depth: 0; }
.tree ul > li > .tree__node { --depth: 1; }
.tree ul ul > li > .tree__node { --depth: 2; }
.tree ul ul ul > li > .tree__node { --depth: 3; }

/* 6.3 Tree reveal.
   The hidden state is scoped to .tree-reveal-enabled, which assets/js/tree.js
   sets on <html>. Without the script the chart simply renders. */

.tree-reveal-enabled .tree__node,
.tree-reveal-enabled .tree li::before,
.tree-reveal-enabled .tree li::after,
.tree-reveal-enabled .tree ul::before {
  opacity: 0;
}

/* Longhand, so the reveal's staggered delay applies to opacity and transform
   only and the hover colour keeps its own immediate timing. A shorthand here
   would give border-color the stagger too, and the path would light up a
   third of a second after the pointer arrived. */
.tree-reveal-enabled .tree__node {
  transform: translateY(var(--space-2));
  transition-property: opacity, transform, border-color;
  transition-duration: 420ms, 420ms, 200ms;
  transition-timing-function: ease-out, var(--ease-out), var(--ease-out);
  transition-delay:
    calc(var(--depth, 0) * 130ms + 60ms),
    calc(var(--depth, 0) * 130ms + 60ms),
    0s;
}

.tree-reveal-enabled .tree li::before,
.tree-reveal-enabled .tree li::after,
.tree-reveal-enabled .tree ul::before {
  transition-property: opacity, border-color;
  transition-duration: 300ms, 200ms;
  transition-timing-function: ease-out, var(--ease-out);
}

/* Connectors fade in just before the nodes they lead to. The second value is
   border-color's delay, held at 0s so hover stays immediate. */
.tree-reveal-enabled .tree ul::before { transition-delay: 60ms, 0s; }
.tree-reveal-enabled .tree li::before,
.tree-reveal-enabled .tree li::after { transition-delay: 140ms, 0s; }
.tree-reveal-enabled .tree ul ul::before { transition-delay: 190ms, 0s; }
.tree-reveal-enabled .tree ul ul li::before,
.tree-reveal-enabled .tree ul ul li::after { transition-delay: 270ms, 0s; }

.tree-reveal-enabled .tree.is-revealed .tree__node {
  opacity: 1;
  transform: none;
}

.tree-reveal-enabled .tree.is-revealed li::before,
.tree-reveal-enabled .tree.is-revealed li::after,
.tree-reveal-enabled .tree.is-revealed ul::before {
  opacity: 1;
}



/* ==========================================================================
   7. DISCLOSURE
   ========================================================================== */

/* Small print, set to read as small print. The line height is opened well
   past body copy to keep the smallest step in the scale legible. */
.disclosure {
  max-width: var(--text-measure);
  margin: var(--space-6) 0 0;
  color: var(--color-muted);
  font-size: var(--size-legal);
  line-height: 1.75;
  text-wrap: pretty;
}

.disclosure p { margin: 0 0 var(--space-3); }
.disclosure p:last-child { margin-bottom: 0; }


/* ==========================================================================
   8. FOOTER
   Continuous with the page — a rule, not a slab.
   ========================================================================== */

.site-footer {
  padding: var(--space-8) 0 var(--space-7);
  border-top: var(--border-width) solid var(--color-line);
  color: var(--color-muted);
  font-size: var(--size-footer);
}

.footer-layout {
  display: grid;
  grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr));
  gap: var(--space-6) var(--space-7);
}

.footer-layout p { margin: var(--space-1) 0 0; }

.footer-column__title {
  margin: 0 0 var(--space-3);
  color: var(--color-ink);
  font-size: var(--size-label);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.site-footer a {
  color: var(--color-ink-soft);
  text-underline-offset: var(--space-1);
}

.site-footer a:hover { color: var(--color-ink); }

/*
 * The legal notice block: one column, running the full width of the shell.
 *
 * One column, because these paragraphs were briefly set two-up and grid fills
 * row by row — column one held notices 1 and 3, column two held 2 and 4, so
 * reading down one column and then the other takes them in the order
 * 1, 3, 2, 4. Where sequence carries legal meaning that is not a layout
 * preference, it is wrong. A single column can only be read as written.
 *
 * Full width, because the block overrides the .disclosure measure that suits
 * this text inside a page section. Here it is the footer's own band, and a
 * short column left the rule above it stopping mid-air against the full-width
 * rule under the copyright line. Long lines are the accepted trade for
 * boilerplate; --line-body is already opened up to 1.75 to carry the eye back.
 */
.site-footer .disclosure {
  max-width: none;
  margin: var(--space-7) 0 0;
  padding-top: var(--space-5);
  border-top: var(--border-width) solid var(--color-line);
}

.footer-legal {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--color-line);
  color: var(--color-muted);
  font-size: var(--size-legal);
}


/* ==========================================================================
   9. RESPONSIVE
   Breakpoints match numtrade.in: 56rem, 52rem, 44rem, 31rem.
   ========================================================================== */

@media (max-width: 56rem) {
  .split-layout {
    grid-template-columns: 1fr;
    gap: var(--space-7);
  }

  /*
   * The tree turns vertical. Same markup: the sibling bar becomes a spine
   * running down the left of each branch, and each node gets an elbow into it.
   *
   * This switches here rather than at the phone breakpoint because the leaf
   * columns are the chart width divided by six — two top-level branches, three
   * children under one of them. Below roughly this width that arithmetic
   * leaves each leaf too narrow to set "CyberExchange / Research programme"
   * without shredding it into stacked fragments. The vertical form has no such
   * limit: every node gets the full measure whatever the viewport.
   */
  .tree-wrap { overflow-x: visible; }

  /* Plain block flow: there is no row to centre, so the equal-column grid and
     the centring flex both come off. */
  .tree li {
    display: block;
    padding-top: 0;
    padding-left: var(--space-6);
  }

  .tree > li { padding-left: 0; }

  .tree li + li { margin-top: var(--space-4); }

  .tree ul {
    display: block;
    width: auto;
    padding-top: var(--space-4);
    padding-left: var(--space-4);
  }

  .tree ul::before { display: none; }

  /* Elbow into the node. */
  .tree li::before {
    top: var(--space-5);
    right: auto;
    left: 0;
    width: var(--space-6);
    height: 0;
    border-top: var(--border-width) solid var(--color-line);
  }

  /* Spine: full height for every child except the last, which stops at its
     own elbow so the line does not dangle. */
  .tree li::after {
    top: 0;
    left: 0;
    width: 0;
    height: calc(100% + var(--space-4));
    border-top: 0;
    border-left: var(--border-width) solid var(--color-line);
  }

  .tree li:first-child::before { border-top: var(--border-width) solid var(--color-line); }
  .tree li:last-child::after { height: var(--space-5); }

  .tree__node {
    width: 100%;
    text-align: left;
  }
}

@media (max-width: 52rem) {
  .site-header__inner {
    justify-content: flex-start;
    padding-top: var(--space-3);
    padding-bottom: var(--space-3);
  }

  .site-nav--desktop { display: none; }

  .site-menu {
    display: block;
    position: static;
    align-self: center;
    flex: 0 0 auto;
    margin-left: auto;
  }

  .site-menu > summary {
    display: inline-flex;
    min-width: 5.5rem;
    min-height: var(--control-min-height);
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: var(--border-width) solid var(--color-ink);
    cursor: pointer;
    font-size: var(--size-label);
    font-weight: 700;
    letter-spacing: var(--tracking-label);
    list-style: none;
    text-transform: uppercase;
  }

  .site-menu > summary::-webkit-details-marker { display: none; }

  /* `rotate`, not `transform`: the bars use `translate` to centre themselves,
     and keeping the two on separate properties means neither overwrites the
     other. Transition lives on the base rule so it animates closed as well. */
  .site-menu[open] .menu-toggle-mark { rotate: 45deg; }

  /*
   * Backdrop. A fixed pseudo-element on the open menu blurs and dims the page
   * behind the panel. It sits below the summary and the panel but above the
   * page, so the control that opened it stays legible and clickable.
   */
  .site-menu[open]::before {
    content: "";
    position: fixed;
    z-index: 1;
    inset: 0;
    background: var(--color-scrim);
    backdrop-filter: blur(14px) saturate(120%);
    -webkit-backdrop-filter: blur(14px) saturate(120%);
    animation: backdrop-in 240ms ease-out both;
  }

  /* The header stays crisp above the backdrop — only the page behind blurs. */
  .brand,
  .site-menu > summary {
    position: relative;
    z-index: 3;
  }

  .site-menu > .site-nav {
    display: none;
    position: absolute;
    z-index: 3;
    top: calc(100% + var(--border-width));
    right: 0;
    width: 100%;
    grid-template-columns: 1fr;
    justify-content: stretch;
    gap: 0;
    padding: var(--space-2);
    background: var(--color-paper);
    border: var(--border-width) solid var(--color-ink);
  }

  .site-menu[open] > .site-nav {
    display: grid;
    animation: menu-panel-in 260ms var(--ease-out) both;
  }

  .site-nav a {
    display: flex;
    min-height: var(--control-min-height);
    align-items: center;
    padding: var(--space-3);
    border-bottom: var(--border-width) solid var(--color-line);
  }

  .site-nav a:last-child { border-bottom: 0; }

  /* Anchors stagger in behind the panel. The index comes from nth-child, not
     an inline style attribute, for the CSP reason noted at .tree__node depth.
     Six covers any realistic nav; a seventh link simply starts with the sixth
     link's delay rather than breaking. */
  .site-menu[open] > .site-nav a {
    animation: menu-item-in 300ms var(--ease-out) both;
    animation-delay: calc(var(--i, 0) * 55ms + 90ms);
  }

  .site-menu > .site-nav a:nth-child(1) { --i: 0; }
  .site-menu > .site-nav a:nth-child(2) { --i: 1; }
  .site-menu > .site-nav a:nth-child(3) { --i: 2; }
  .site-menu > .site-nav a:nth-child(4) { --i: 3; }
  .site-menu > .site-nav a:nth-child(5) { --i: 4; }
  .site-menu > .site-nav a:nth-child(n + 6) { --i: 5; }
}

@keyframes backdrop-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes menu-panel-in {
  from {
    opacity: 0;
    transform: translateY(calc(var(--space-2) * -1));
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes menu-item-in {
  from {
    opacity: 0;
    transform: translateX(var(--space-3));
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (max-width: 44rem) {
  .hero {
    padding-top: var(--space-9);
    padding-bottom: var(--space-9);
  }

  .section { padding: var(--space-8) 0; }

  .footer-layout { grid-template-columns: 1fr; }

  /* The numbering rail collapses; numbers sit above the step instead. */
  .step {
    grid-template-columns: 1fr;
    gap: var(--space-2);
  }
}

@media (max-width: 31rem) {
  body { --page-gutter: 1rem; }

  .site-header__inner { gap: var(--space-3); }
}

@media (max-width: 22.5rem) {
  body { --size-hero: 2rem; }

  .site-header__inner { gap: var(--space-2); }
}


/* ==========================================================================
   10. REDUCED MOTION
   Everything above still works with motion removed: the tree renders drawn,
   the menu opens without a transition. The blur stays — it is depth, not
   movement.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .tree-reveal-enabled .tree__node,
  .tree-reveal-enabled .tree li::before,
  .tree-reveal-enabled .tree li::after,
  .tree-reveal-enabled .tree ul::before {
    opacity: 1;
    transform: none;
    transition: none;
  }

  /* The path still lights on hover — it is information, not decoration. It
     just arrives at once instead of fading in. */
  .tree__node,
  .tree li::before,
  .tree li::after,
  .tree ul::before { transition: none; }

  .site-menu[open]::before,
  .site-menu[open] > .site-nav,
  .site-menu[open] > .site-nav a {
    animation: none;
  }

  .site-menu[open] .menu-toggle-mark { transition: none; }
}
