*, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; background: #131618; }
  html { scroll-behavior: smooth; }
  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    /* the header still changes state, it just stops animating its way there */
    [data-r="nav"], [data-r="nav"] *, [data-r="navbtn"] span { transition-duration: 1ms !important; }
  }

  a { color: #e8e6e1; text-decoration: none; }
  /* the CTA is deliberately not in this list: it inverts rather than tinting,
     and it is driven from its container — see [data-r="navcta"]:hover below */
  a:hover, [data-r="navlinks"] a:hover { color: #c08b37; }
  /* a hint, not a second label. this used to be #8b9198 — the exact value of
     --label — so the placeholder and the word above it were the same colour in
     the same typeface, and the pair read as two equal things rather than a
     field and its name. dropped to about 3:1 on --raise against the label's
     5:1, which is half the luminance and reads as clearly beneath it. it can go
     this quiet because no meaning lives here: every field keeps a permanent
     label, so the placeholder is only ever an example of what to type.

     Colour alone did not settle it: the hint inherited the field's 16px and so
     sat 45% larger than the 11px label it was meant to defer to, and size was
     shouting louder than tone was whispering. 13px is the step that reads as a
     hint at a glance while still being comfortable in a 44px field — 11.5px,
     level with the label, starts to look like a rendering fault.

     The size lives here rather than on the input because the field itself
     cannot move: anything under 16px makes iOS zoom the page on focus. So the
     placeholder is deliberately smaller than the text that replaces it, and
     what you type grows a little as you type it. That is the cheaper of the
     two costs. */
  input::placeholder, textarea::placeholder { color: #686e75; font-size: 13px; opacity: 1; }
  input:focus, textarea:focus { border-bottom-color: #c08b37; }
  [data-r="send"]:hover { background: #cf9a44; }
  a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible {
    outline: 2px solid #c08b37; outline-offset: 3px;
  }

  /* ---- responsive: the visualisations ------------------------------------
     Every element on this page carries its own inline style, so the only way
     a stylesheet can restructure one is with !important. Layout hooks are
     data-r attributes; nothing here touches type or colour. */

  /* ---- the header, in two states ----------------------------------------
     The nav is fixed and starts with no ground of its own, so the photograph
     runs under it and the page opens as one image rather than as a bar with a
     picture below it. Everything that changes between the two states lives
     here; site.js only flips data-solid, the same arrangement as data-open.

     Over the photograph the links go up from --dim to near-ink. --dim was
     chosen against a flat #1a1d1f ground and does not survive a photograph,
     even one under a scrim.

     The crossing is short at 220ms. A slow ground reads as the bar fading in
     late — the state has already changed by the time the eye reaches it,
     which looks like a bug rather than a transition.

     Carrying the glass over the hero as well, so there is no change at all,
     was tried and is worse: it puts a permanent band across the photograph,
     which is the thing this arrangement exists to avoid. The change is the
     price of the clean hero, and it is the cheaper of the two. */
  /* ---- where the bar lives ------------------------------------------------
     Fixed at every width, and --bar-off is the height of what is parked over
     the content. Anchors, the pinned panel's sticky top and the context track's
     height all read the one variable, so they cannot disagree with each other.

     It is declared here and NOT in index.html's token block: that block is an
     inline style, and an inline declaration outranks any rule in this file, so
     a value that ever has to change with width cannot live there. */
  [data-r="nav"] { position: fixed; top: 0; left: 0; right: 0; z-index: 20; }
  /* Air between the fixed bar and anything that parks under it. It applies at
     every width — the desktop pin and the phone's accordion headers both stick
     to the same edge — so it lives here, once, and both read it.

     It is NOT free: whatever this is, the pinned composition has that much less
     viewport to fit inside, and site.js only pins when the tallest panel fits.
     The pinned margin reductions further down claw back about 24px and that is
     what lets a 1366x768 laptop pin at all, so a gap of that size hands it
     straight back. Raise it and re-check that machine. */
  [data-r="root"] { --bar-off: var(--nav-real, var(--nav-h)); --ctx-gap: 24px; }

  /* Neither the ground nor the link colour is transitioned any more: both are
     driven off scroll position now, and a 220ms ease on a value that changes
     every frame only makes the bar lag the page it is sitting on. The border
     keeps its transition — that one is still a state change. */
  [data-r="nav"] { transition: border-color 220ms ease; }
  [data-r="nav"] [data-r="navlinks"] { color: rgba(232, 230, 225, 0.86); }

  /* The bar gathers its ground continuously instead of switching to it. site.js
     writes --nav-p, 0 to 1 across the first 80px of scroll, and every property
     that changes reads that one number — so there is no threshold to catch out
     of the corner of your eye, which is the whole complaint against the version
     this replaces. Past the hero the two look identical; the difference lives
     entirely in the transition.

     The old rule also transitioned background-color but not backdrop-filter,
     so the blur snapped on in a single frame while the tint faded over 220ms.
     Driving both off scroll position removes that mismatch by construction.

     The ground stops at 0.62 rather than going opaque because the blur is the
     point: at full opacity there is nothing behind it to blur and it collapses
     into a plain bar. No hairline — the blur already separates it from the
     page, and a rule on top of that reads as heavy.

     data-glass rather than an unconditional filter: backdrop-filter: blur(0)
     is still a compositing layer over the photograph for the whole of the
     hero. It switches on at p > 0.01, where the blur is 0.16px and nothing
     visible happens. */
  /* translucent rather than dark: a light tint over a strong blur, so what
     scrolls underneath stays visible as colour and shape */
  [data-r="nav"] { background: rgba(20, 23, 26, calc(var(--nav-p, 0) * 0.22)); }
  [data-r="nav"][data-glass] {
    -webkit-backdrop-filter: blur(calc(var(--nav-p, 0) * 12px)) saturate(1.15);
    backdrop-filter: blur(calc(var(--nav-p, 0) * 12px)) saturate(1.15);
  }

  /* the links travel with it. the binary rule stays first as the fallback: a
     browser without color-mix drops the declaration below and gets the step
     change, which is what shipped before and is no worse than it was. */
  [data-r="nav"][data-solid="1"] [data-r="navlinks"] { color: var(--dim); }
  [data-r="nav"] [data-r="navlinks"] {
    color: color-mix(in srgb, var(--dim) calc(var(--nav-p, 0) * 100%), rgba(232, 230, 225, 0.86));
  }
  /* the button's hairline is invisible against a photograph */
  [data-r="nav"] [data-r="navbtn"] { border-color: rgba(232, 230, 225, 0.38); transition: border-color 220ms ease; }
  [data-r="nav"][data-solid="1"] [data-r="navbtn"] { border-color: var(--rule); }
  /* The CTA fills on hover, so the label has to invert with it or it is brass on
     brass — a solid block with the words gone.

     Both properties are set on the container rather than on the anchor, because
     the anchor carries an inline `color: inherit` and an inline declaration beats
     any rule in this file. Colouring the anchor directly therefore does nothing;
     colouring what it inherits from always works. The background sits here too so
     the pair cannot drift apart.

     The resting colour had to come out of the container's inline style for the
     same reason — while it lived there it outranked the hover rule, so the label
     stayed brass on a brass fill and the button read as a blank block. Anything
     that needs a hover state cannot keep that property inline. */
  [data-r="navcta"] { color: var(--accent); transition: background-color 160ms ease, color 160ms ease; }
  [data-r="navcta"]:hover { background: var(--accent); color: var(--on-accent); }

  /* an open menu always takes a ground, wherever the page happens to be
     scrolled to — the panel drops over the photograph otherwise */
  [data-r="nav"][data-open="1"] { background: var(--ground); border-bottom-color: var(--rule); }

  /* the header collapses behind a button before its links would ever wrap, so
     the fixed bar stays one row at every width and --nav-h keeps holding —
     a second row would have hidden every anchored heading underneath it.

     1000 rather than 1080 so a 1024px laptop keeps the full bar, which is what
     the shorter CTA bought: "Request a working session" is 25 characters at
     12px mono and ~238px with its padding, "Book a demo" is 11 and ~124px. At
     1024 the gutter is 53.2 a side, leaving 917.6 for a row that now needs
     about 896. It fits with roughly twenty pixels to spare, so this breakpoint
     and that string length are tied together — lengthen the CTA and the bar
     wraps here before the media query ever catches it.

     site.js watches the same number when it closes an open menu on resize. */
  @media (max-width: 1000px) {
    [data-r="nav"] { padding-top: 14px !important; padding-bottom: 14px !important; }
    [data-r="navbtn"] { display: flex !important; order: 2; }
    [data-r="navcta"] { order: 4; }
    [data-r="navlinks"], [data-r="navcta"] { display: none !important; }
    [data-r="nav"][data-open="1"] [data-r="navlinks"] { display: flex !important; order: 3; flex-basis: 100% !important; flex-direction: column; gap: 0 !important; padding-top: 6px; }
    [data-r="nav"][data-open="1"] [data-r="navlinks"] a { display: flex; align-items: center; min-height: 44px; }
    [data-r="nav"][data-open="1"] [data-r="navcta"] { display: block !important; flex-basis: 100% !important; margin-right: auto; }
    [data-r="nav"][data-open="1"] [data-r="navcta"] a { display: flex; align-items: center; min-height: 44px; }
    [data-r="nav"][data-open="1"] [data-r="navbtn"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
    [data-r="nav"][data-open="1"] [data-r="navbtn"] span:nth-child(2) { opacity: 0; }
    [data-r="nav"][data-open="1"] [data-r="navbtn"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
  }

  /* Since 2026-10-01 the hero carries copy again (eyebrow, headline, kicker,
     claims, buttons) at the foot of the frame, under a diagonal veil set inline
     on [data-r="heroveil"]. The contrast figures below were measured for the
     old one-line hero and flat veil, and need re-measuring for this one.

     The photograph is the hero's background, not a band of its own. That makes
     the frame full-bleed at every width and the crop, rather than the ratio, the
     only thing that moves — so `sizes` is a flat 100vw and the <picture> ladder
     picks the rung on width alone.

     The image is eager and fetchpriority=high: it is almost certainly the LCP
     element now, and the lazy attribute it carried as a below-fold band would
     have held the largest paint until after layout.

     Two treatments stack, and they do different jobs. The filter on the <img>
     pulls the whole frame down into the page's value range while keeping the
     window lights near --accent, which is what ties the photograph to the
     palette. The veil over it is a vertical gradient, heaviest at the top where
     the headline sits and lightest at the bottom where nothing does — the sky is
     the brightest part of this frame and the words are laid straight over it, so
     the darkening has to be strongest exactly there. Flattening the veil to a
     uniform tint is what makes the type start to swim.

     A THIRD layer sits over the top 190px: [data-r="navscrim"]. It is currently
     dialled to zero and so does nothing — the element is kept because it is the
     only knob for the header's ground, and reinstating it is one number. When
     it does carry a value, remember that darkening layers multiply rather than
     add: light through two of them is (1 - a)(1 - b), so it has to be read
     together with the veil underneath rather than sized on its own. At 0.80
     over the old 0.76 veil the very top of the frame came out at 4.8% light —
     black, with a skyline implied beneath it.

     Measured against the current values, the nav links sit at 4.5:1 with no
     scrim and 7.6:1 at 0.52. 4.5 clears the requirement for their size, but it
     clears it on THIS crop; a lighter frame behind the header would not have
     that margin.

     Contrast floor: the hero is one line of large type and nothing else, so
     everything on the photograph is display size and wants 3:1 rather than 4.5.
     It does not clear it. Measured on the brightest 0.5% of pixels behind the
     h1, with the veil as it stands, --ink is 2.4:1. Set D bought that — see
     HERO-VALUES.md, which records the flat veil as brightness taken in exchange
     for both blocks — and the lever is the veil rather than the type: flat 0.65
     gives 4.8:1, flat 0.75 gives 6.6. --ink against these pixels tops out at
     2.37:1, so nothing laid on this frame in any colour reaches the floor while
     the veil stays flat at 0.40; --accent, for the record, is 1.0:1.

     The 4.5:1 problem this comment used to describe at length is gone with the
     block that had it. The hero carried a lead paragraph sitting at 71-88% of
     the frame, below the veil's 42% dark band and therefore unprotected by it;
     that paragraph now lives in [data-r="intro"] below, on flat --ground. If
     body copy ever comes back onto this frame it inherits the problem rather
     than a clean slate, and the fix measured for it was stops of
     0.40 / 0.50 / 0.65 / 0.50 — the band moved down to where the text is.

     object-position is the vertical bias inside the crop; keep it in step with
     `position` in tools/build-photo.js. It sits low so the skyline fills the
     lower half of the hero, under the text rather than behind it. */
  @media (max-width: 700px) {
    /* The phone is served a different negative: a 2:3 portrait cut from the same
       master (tools/build-photo-mobile.py), matched by the <source media> at this
       same 700px. The two numbers must agree — change one, change the other.

       Its object-position is not the wide file's. The portrait crop is taller
       than the hero box, so cover trims it horizontally and the Y value idles;
       it earns its keep only on a short landscape handset, where the box turns
       wider than 2:3 and the trim becomes vertical instead. 42% is chosen for
       that case: it holds the spire and gives up foreground, which is the right
       thing to lose. */
    [data-r="herowrap"] img { object-position: 50% 42% !important; }

    /* On a phone the copy spans the whole frame, so the desktop's diagonal
       veil (dark left, open right) would leave half of it on bright sky. Here
       it is one vertical veil, heavy all the way down and fading into
       --ground at the foot like the desktop one. */
    [data-r="heroveil"] {
      background: linear-gradient(180deg, rgba(14,18,22,0.62) 0%, rgba(14,18,22,0.72) 45%, rgba(28,33,38,0.97) 100%) !important;
    }

    /* The hero's phone rule used to live here: the chip was 39 monospace
       characters on one line and needed a measured step down in size and
       tracking to survive 360px. The chip is gone — the hero is a serif
       headline now, which wraps where it likes — so the rule went with it. The
       measurements are in the history if a monospace one-liner ever returns. */
  }

  /* market context: four contexts on one level. which one is open is
     aria-expanded on its button, so the stylesheet owns every rule that changes
     with it — same arrangement as the nav's data-open. the four short names ARE
     the argument, so all four stay visible at every width; only their
     arrangement changes. disclosure semantics rather than tabs, because the
     phone layout below is a genuine accordion and swapping ARIA roles at a
     breakpoint is a good way to ship something subtly broken. */
  [data-r="tabs"] button {
    border: 0;
    border-bottom: 2px solid var(--rule);
    color: var(--dim);
    transition: color 170ms ease, border-color 170ms ease;
  }
  [data-r="tabs"] button:hover { color: var(--body); border-bottom-color: var(--field); }
  [data-r="tabs"] button[aria-expanded="true"] { color: var(--ink); border-bottom-color: var(--accent); }
  [data-r="tabs"] button:focus-visible { outline: 1px solid var(--accent); outline-offset: 4px; }
  /* the panels carry an inline display:grid, so hidden needs the weight to win */
  [data-panel][hidden] { display: none !important; }

  /* ---- pinned: scroll changes the context -------------------------------
     Above 1000px the section holds still and the contexts move through it.
     #context is the track and [data-r="pin"] is what sticks inside it; the
     track is given four steps of height it does not need, and the sticky child
     spends the whole of it held under the header before releasing on its own.

     That height has to land in #context's CONTENT box. A sticky element is
     constrained by its containing block's content box, and padding is no part
     of it: growing padding-bottom makes the section taller and gives the pin
     nowhere at all to travel, which shows up as a pin that holds for a single
     frame at the top of the section and then scrolls away like any other
     block. box-sizing is border-box here, so the padding is added back into
     min-height to leave exactly the intended content height behind.

     The viewport term is an upper bound on the pin's own height (it is only
     ever pinned when it fits), so the travel is never shorter than the four
     steps. Whatever the pin does not use falls at the end, and the last
     context holds a little longer than the other three — which is the right
     place for the slack.

     Four steps for four contexts, so the last one is read for a full step
     before the section lets go. Everything is gated on data-mode, which site.js
     only sets once it has measured that the tallest panel actually fits the
     viewport: on a short laptop, and under reduced motion, none of this
     applies and the strip stays the plain click-to-switch strip it was.

     --ctx-step (index.html, the token wrapper) is the one number to turn: it
     is how far you scroll per context. */
  @media (min-width: 1001px) {
    /* The induction runs 2×2 rather than as a column of four. It is the AI
       panel's aside, the AI panel is the tallest of the four, and the tallest
       of the four is what every machine's fit test is decided on — a column
       here costs about 190px and it is the difference between a 13" laptop
       pinning and falling back to the click strip. Nothing is cut for it; the
       aside is 580px wide at this breakpoint and two 270px columns of a 17.5px
       quote read perfectly well.

       The rules carry their own hairlines inline, which are right in a column
       and wrong in a grid: the four would show three borders and a gap. The
       top row keeps one as a divider between the rows, the bottom row loses
       it, and the closing line spans both. */
    [data-r="induct"] { display: grid; grid-template-columns: 1fr 1fr; column-gap: 30px; }
    [data-r="induct"] > div { border-bottom: 0 !important; }
    [data-r="induct"] > div:nth-child(-n+2) { border-bottom: 1px solid var(--rule-soft) !important; }
    [data-r="induct"] > div:last-child { grid-column: 1 / -1; }

    #context[data-mode="pin"] {
      min-height: calc(var(--sec-t) + var(--sec-b) + (100vh - var(--bar-off) - var(--ctx-gap)) + 4 * var(--ctx-step));
    }

    /* Script off. Everything above needs site.js, so what is left is the four
       panels one under another with the names in a strip at the top that no
       longer points at anything — and, on this breakpoint only, nothing to
       separate one context from the next: the phone gets its headers between
       them for free from the accordion layout, the desktop does not, and the
       four run together into a single column of prose. A rule and some air is
       enough to make them read as four again. */
    #context:not([data-live="1"]) [data-panel] + [data-panel] {
      margin-top: clamp(40px, 4vw, 64px);
      padding-top: clamp(40px, 4vw, 64px);
      border-top: 1px solid var(--rule);
    }
    /* --bar-off is 60px here, not zero: the bar became fixed at every width
       when it started fading in over the page, so it is still parked over this
       section when the reader arrives and the panel has to sit under it. The
       same variable is read by site.js when it works out where the pin takes
       hold, so the sticky top and the scroll origin cannot drift apart — which
       is the only reason this stays a variable rather than a number. */
    #context[data-mode="pin"] [data-r="pin"] { position: sticky; top: calc(var(--bar-off) + var(--ctx-gap)); }

    /* pinned, the strip and the stage are one held composition rather than a
       heading with a section under it, and they want less air between them
       than the scrolling layout does. this is worth ~24px, which is exactly
       what a 1366×768 laptop is short of — the difference between that machine
       pinning and dropping to the click strip. both values are inline, hence
       the weight. */
    #context[data-mode="pin"] [data-r="pin"] > div:first-child { margin-bottom: 12px !important; }
    #context[data-mode="pin"] [data-r="tabs"] { margin-bottom: clamp(24px, 2.4vw, 34px) !important; }

    /* the four panels overlaid in one cell. the stage takes the height of the
       tallest of them and holds it, which is what stops the page shifting as
       they change — the job the old min-height floor was approximating, done
       exactly and without a magic number. */
    #context[data-mode="pin"] [data-r="stage"] { display: grid; }
    #context[data-mode="pin"] [data-panel] {
      grid-area: 1 / 1;
      opacity: 0;
      transform: translateY(10px);
      /* visibility, not opacity alone: a transparent panel is still findable
         by ctrl-F, and being scrolled to a panel nobody can see is worse than
         not finding it. it is transitioned at 0s on a delay so it flips only
         once the fade is over. */
      visibility: hidden;
      transition: opacity 420ms ease, transform 420ms ease, visibility 0s linear 420ms;
    }
    #context[data-mode="pin"] [data-panel][data-on] {
      opacity: 1;
      transform: none;
      visibility: visible;
      transition-delay: 0s;
    }
    @media (prefers-reduced-motion: reduce) {
      #context[data-mode="pin"] [data-panel] { transition-duration: 1ms; transform: none; }
    }

    /* No scrub here. The underline used to fill across the live context as you
       scrolled through its step, on the theory that a strip answering the
       scroll continuously would read as progress. It reads as almost nothing:
       2px creeping over 300px is far too quiet to be the only evidence that
       scrolling is still doing something, and a reader who does not catch it
       concludes the page has ended — which is the whole cost of pinning, paid
       for a signal most people never see.
       The panel changing is the loud signal, and it is now frequent enough
       (--ctx-step) to be the answer on its own. The strip goes back to saying
       which context is up, full accent rule, exactly as it does in the click
       and accordion layouts — one behaviour across all three instead of a
       fourth that only exists while pinned. --fill still drives the phone's
       pager, where a finger genuinely is part-way between two cells. */
  }

  /* ---- narrow: the four contexts become a swipe carousel -----------------
     Below 1000px a panel is 690–1150px against roughly 650px of screen, so the
     pinned stage cannot be had honestly. Stacking all four open was the first
     answer and it worked, but it cost five screens of scroll and the contexts
     stopped changing as you moved — the whole point of the desktop mechanic
     died on the phone.

     So the panels go side by side and the phone's own gesture does the work:
     native x scroll-snap, one context per screen, the next one peeking at the
     edge so there is something to reach for. The strip goes back to being a
     strip and becomes the pager, its four underlines crossfading with the
     scroll position rather than one of them switching on at the end. Nothing
     is cut and nothing is behind a tap; the section goes from five screens to
     about one and a half.

     Vertical scroll is untouched — the carousel scrolls in the axis the page
     does not, so neither has to be taken away from the other.

     Both layouts here are keyed off data-mode, which site.js sets; the stack
     below is what is left when it never runs. The pin rules further up are
     gated on the other side of the same 1000px. All three must agree — change
     one, change the others. */
  @media (max-width: 1000px) {
    #context[data-mode="swipe"] [data-r="tabs"] {
      display: grid !important;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 0 10px !important;
      margin: 0 0 24px !important;
    }
    /* the pager cell: just the name, allowed a second line — "FUND
       STRUCTURES" does not fit a quarter of a 390px screen on one. */
    #context[data-mode="swipe"] [data-r="tabs"] button {
      position: relative;
      display: flex !important;
      flex-direction: column;
      align-items: flex-start !important;
      gap: 5px !important;
      width: 100%;
      padding: 0 0 10px !important;
      background: transparent !important;
      border-bottom-width: 2px;
    }
    #context[data-mode="swipe"] [data-r="tabs"] button [data-chev] { display: none !important; }
    #context[data-mode="swipe"] [data-r="tabs"] button [data-name] {
      font-size: 10.5px !important;
      letter-spacing: 0.04em !important;
      line-height: 1.3;
    }
    #context[data-mode="swipe"] [data-r="tabs"] button[aria-current="true"] { color: var(--ink); }
    /* every cell carries the scrub, not just the live one: mid-swipe two of
       them are part-lit, which is what makes the pager read as a position
       rather than a selection */
    #context[data-mode="swipe"] [data-r="tabs"] button::after {
      content: "";
      position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
      background: var(--accent);
      transform: scaleX(var(--fill, 0));
      transform-origin: left center;
    }

    #context[data-mode="swipe"] [data-r="stage"] {
      display: grid;
      grid-auto-flow: column;
      /* exactly the content width, no peek. a sliver of the next context was
         tried and it lands on that panel's first characters, so it reads as
         text clipped by a bug rather than as more to come; fading it out was
         worse still, because the same gradient dims the right-hand column of
         the stress matrix, which carries the numbers the figure exists for.
         the pager above is the affordance — four cells with one lit. */
      grid-auto-columns: 100%;
      gap: 14px;
      overflow-x: auto;
      overscroll-behavior-x: contain;
      scroll-snap-type: x mandatory;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
    }
    #context[data-mode="swipe"] [data-r="stage"]::-webkit-scrollbar { display: none; }
    /* The carousel is one grid row, so its height is the tallest context and
       the other three sit over that much empty ground. The induction is what
       makes the fourth an outlier — 600px of it against the 330px canvas that
       sets the second — and most of that is the gloss under each rule.

       The rules themselves all stay. What goes is the second-order line
       explaining each one, which is the part a phone reader is least likely to
       be reading closely and the only part whose loss costs no argument: the
       quote above it already makes the point. Worth ~190px, and it takes the
       spread across the four from 470px down to under 300. */
    [data-gloss] { display: none !important; }

    #context[data-mode="swipe"] [data-panel] {
      scroll-snap-align: start;
      padding: 0 !important;
      min-width: 0;
      /* the carousel is one grid row, so every cell would otherwise be stretched
         to the tallest of the four. the aside centres itself inside whatever
         height it is given, so a stretched short panel puts its figure hundreds
         of pixels below the prose that introduces it. each context takes its
         own height instead and the row keeps the tallest. */
      align-self: start;
    }

    /* ---- script off: the four contexts stack, each under its own header ----
       The markup does not move. `display: contents` dissolves the strip so its
       buttons become children of the section's own grid alongside the panels,
       and `order` interleaves them into header/panel pairs. */
    #context:not([data-mode="swipe"]) { display: grid; grid-template-columns: minmax(0, 1fr); }
    /* the strip carries an inline display:grid, so dissolving it needs the weight */
    #context:not([data-mode="swipe"]) [data-r="tabs"] { display: contents !important; }
    /* and so do the two pinning wrappers: neither has a job at this width, and
       the stage's grid would otherwise stack the panels on top of each other */
    #context:not([data-mode="swipe"]) [data-r="pin"],
    #context:not([data-mode="swipe"]) [data-r="stage"] { display: contents !important; }

    /* each button immediately above the panel it opens; the eyebrow keeps the
       default order: 0 and so stays on top */
    #context:not([data-mode="swipe"]) [data-tab="regulation"] { order: 1; }
    #context:not([data-mode="swipe"]) [data-panel="regulation"] { order: 2; }
    #context:not([data-mode="swipe"]) [data-tab="risk"] { order: 3; }
    #context:not([data-mode="swipe"]) [data-panel="risk"] { order: 4; }
    #context:not([data-mode="swipe"]) [data-tab="structure"] { order: 5; }
    #context:not([data-mode="swipe"]) [data-panel="structure"] { order: 6; }
    #context:not([data-mode="swipe"]) [data-tab="ai"] { order: 7; }
    #context:not([data-mode="swipe"]) [data-panel="ai"] { order: 8; }

    /* name | chevron on one line. the button's inline style sets align-items
       and gap, so both need the weight to be overridden here. */
    #context:not([data-mode="swipe"]) [data-r="tabs"] button {
      display: grid !important;
      grid-template-columns: minmax(0, 1fr) auto;
      column-gap: 14px !important;
      align-items: baseline !important;
      width: 100%;
      padding: 17px 0 !important;
      border-bottom-width: 1px;
      position: sticky; top: calc(var(--bar-off) + var(--ctx-gap)); z-index: 2;
      background: var(--ground) !important;
    }
    #context:not([data-mode="swipe"]) [data-r="tabs"] button[aria-expanded="true"] { border-bottom-color: var(--rule); }
    #context:not([data-mode="swipe"]) [data-r="tabs"] button [data-name] { grid-column: 1; grid-row: 1; }
    #context:not([data-mode="swipe"]) [data-r="tabs"] button [data-chev] {
      display: block !important; grid-column: 2; grid-row: 1;
      transition: transform 200ms ease; color: var(--label);
    }
    #context:not([data-mode="swipe"]) [data-r="tabs"] button[aria-expanded="true"] [data-chev] { transform: rotate(180deg); color: var(--accent); }
    #context:not([data-mode="swipe"]) [data-panel] { padding: 26px 0 34px; }
  }

  /* a section and the visual beside it */
  @media (max-width: 1000px) {
    [data-r="split"] { grid-template-columns: minmax(0, 1fr) !important; gap: 34px !important; }
    [data-r="aside"] { border-left: 0 !important; padding-left: 0 !important; border-top: 1px solid var(--rule) !important; padding-top: 28px !important; }
    /* stacked, there is no label beside the heading to line up with */
    [data-r="label-align"] { padding-top: 0 !important; }
  }

  /* the product diagram: the left-to-right chain becomes a top-to-bottom one.
     stacked, the workspace moves BELOW the hub so the page reads in flow order
     — what arrives, where it is kept, who works in it, what reads from it. */
  @media (max-width: 1080px) {
    [data-r="chain"] { grid-template-columns: minmax(0, 1fr) !important; }
    [data-r="chain"] > * { grid-column: auto !important; }
    [data-r="arrow"] { transform: rotate(90deg); padding: 10px 0 !important; }
    [data-r="stack"] { gap: 12px; }
    [data-o="1"] { order: 1; }
    [data-o="2"] { order: 2; }
    [data-o="3"] { order: 3; }
    [data-o="4"] { order: 4; }
  }

  /* three across is the last thing to go */
  @media (max-width: 760px) {
    [data-r="trio"], [data-r="pair"] { grid-template-columns: minmax(0, 1fr) !important; }
  }

  /* ---- the product diagram on a phone ------------------------------------
     Below 760 the chain stops being a chain: eight full-width boxes over
     1245px, with the hub two screens above what comes out of it, and the
     workspace sitting between the two. It is replaced by the hub itself,
     holding a contents page of everything that touches it — 540px, and origin
     is containment rather than adjacency. The same width the trio gives up
     three-across at, so the section changes mode once rather than twice.

     Only the sentences collapse. Every name is on screen unpressed, which is
     the bargain startTabs makes above and for the same reason. */
  @media (max-width: 760px) {
    [data-r="stack"] { display: none !important; }
    [data-r="hubdisc"] { display: block !important; }
  }

  /* the panels carry no inline display, so hidden would work on its own; it is
     stated here to keep the whole behaviour in one place */
  [data-r="hubdisc"] [data-discpanel][hidden] { display: none !important; }
  [data-r="hubdisc"] [data-chev] {
    font-family: var(--font-label);
    transition: transform 200ms ease, color 200ms ease;
  }
  [data-r="hubdisc"] [data-disc][aria-expanded="true"] [data-chev] { transform: rotate(180deg); color: var(--accent); }
  [data-r="hubdisc"] [data-disc]:focus-visible { outline: 1px solid var(--accent); outline-offset: 3px; }
  @media (prefers-reduced-motion: reduce) {
    [data-r="hubdisc"] [data-chev] { transition-duration: 1ms; }
  }

  /* the Excel page's request panel: heading beside a one-row form (name, email,
     button). Below 1200 the form's column is too narrow for three, and stacking
     the form inside it leaves the heading stranded at the foot of a tall empty
     cell — so the panel stacks first, a step earlier than [data-r="split"], and
     the form keeps its row until a phone. */
  @media (max-width: 1200px) {
    [data-r="panelsplit"] { grid-template-columns: minmax(0, 1fr) !important; gap: 24px !important; }
  }
  @media (max-width: 760px) {
    [data-r="formrow"] { grid-template-columns: minmax(0, 1fr) !important; }
  }

  /* the Turnstile slot site.js adds to each form. on the home page it lands
     straight under the send button, so it needs air of its own */
  [data-r="turnstile"] { margin-top: 16px; }

  /* the principles' four quadrants, and the footer's two columns */
  @media (max-width: 760px) {
    [data-r="quad"] { grid-template-columns: minmax(0, 1fr) !important; gap: 14px !important; }
    /* the cells are cards now rather than a full-bleed table, so the padding
       stops being tied to the gutter and just gets smaller */
    [data-r="quad"] > div { padding: 26px 24px !important; }
  }
  /* product.html's short differentiators: a numbered list of seven. Number,
     headline and line sit on one row; on a phone the line drops under the
     headline. */
  @media (max-width: 760px) {
    [data-r="diffrow-i"] { grid-template-columns: 34px minmax(0, 1fr) !important; }
    [data-r="diffrow-i"] > p { grid-column: 2; }
  }
  @media (max-width: 480px) {
    [data-r="quad4"] { grid-template-columns: minmax(0, 1fr) !important; gap: 24px !important; }
  }

  /* a box head puts its qualifier on the next line rather than breaking its
     own title mid-word */
  @media (max-width: 620px) {
    [data-r="boxhead"] > span { white-space: nowrap; }
  }

  /* the three technical layers with private credit running down beside them.
     The column beside the stack is what carries the argument, so it holds its
     150px for as long as the layer names still fit next to it — below that it
     drops under the stack rather than squeezing "Data engineering" onto three
     lines. */
  @media (max-width: 520px) {
    [data-r="disc"] { grid-template-columns: minmax(0, 1fr) !important; }
  }

  /* the stress matrix fits a phone once its cells stop padding themselves out;
     overflow-x stays as a safety net, but nothing should reach for it */
  @media (max-width: 640px) {
    [data-r="matrix"] { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    [data-r="matrix"] > div > span { padding-left: 2px !important; padding-right: 4px !important; }
  }

  /* the valuation-record chart scales with its viewBox, so nothing here needs to
     move it — except the axis labels, which shrink with everything else. At the
     aside's narrowest the 480-unit box lands around 350px, and a 10-unit label
     renders at about seven. Sizing text in user units buys it back. */
  @media (max-width: 640px) {
    [data-r="record"] text { font-size: 13px !important; letter-spacing: 2px !important; }
  }

  /* ---- surfaces -----------------------------------------------------------
     The shipped ladder spanned nine points of luminance — ground 28.5, box 33.6
     — so nothing could sit above anything else, and every object had to be
     drawn with a 1px outline to exist at all. The token block in index.html now
     spans twenty-eight (ground 20, box 36), and that is what lets the outline
     come off: a card is carried by being lighter than what it sits on, which is
     how the eye reads depth everywhere outside a stylesheet.

     The shadow does less work than the surface step and is deliberately two
     layers — a 1px contact shadow that seats the edge, and a wide soft one that
     lifts the whole card. A single mid-range shadow on a dark ground reads as a
     grey halo rather than as height.

     Anything wanting a border still declares one inline; this rule sets no
     border at all, which is why the principles cards keep their accent edge. */
  [data-sf="card"] {
    background: var(--box);
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.28), 0 10px 26px -14px rgba(0, 0, 0, 0.60);
  }

  /* ---- no glass below the hero --------------------------------------------
     There was a [data-sf="glass"] surface here, and a low accent glow laid
     across [data-band] for it to reveal. Both are gone.

     backdrop-filter blurs what is BEHIND an element, so over a flat ground it
     does precisely nothing — the hero chip only reads as glass because a
     photograph sits under it. The glow existed solely to give the blur
     something to sample, which is the argument backwards: the answer to inert
     glass is to stop using glass, not to paint a stain for it to find. What it
     actually produced was a gold cast in the upper right of two sections that
     belonged to no other part of the page.

     The panels are back on real surfaces, declared inline where they were
     before: the hub row on --ground-2 because it CONTAINS three cards and
     should sit below them, the contact panel on --raise because it sits above
     the section. Depth here is carried by the surface ladder and a shadow,
     which is what the rest of the page does. [data-band] survives as the marker
     for the alternating darker ground, which is drawn by its inline
     background — it needs no rule of its own. */

  /* ---- entrance -----------------------------------------------------------
     Scoped to .js-rv, which a one-line script in the <head> adds. Without that
     class none of this applies, so a reader with JavaScript off gets the page
     rather than a column of invisible blocks — the standing cost of hiding
     first and revealing later. It goes on in the head, not on DOMContentLoaded,
     so there is never a frame where the content paints and then vanishes.

     [data-rv=""] is the exact-match for a lone element and [data-rv="stagger"]
     for a group, so the two rule sets cannot overlap and neither depends on
     source order to win. */
  @media (prefers-reduced-motion: no-preference) {
    .js-rv [data-rv=""] {
      opacity: 0;
      transform: translateY(10px);
      transition: opacity 520ms ease, transform 520ms cubic-bezier(.4, 0, .2, 1);
    }
    .js-rv [data-rv=""].rv-in { opacity: 1; transform: none; }

    /* A group reveals its children in sequence. The container itself never
       moves — if it did, the stagger would be fighting a block sliding out from
       under the very children it is trying to sequence. */
    .js-rv [data-rv="stagger"] > * {
      opacity: 0;
      transform: translateY(10px);
      transition: opacity 520ms ease, transform 520ms cubic-bezier(.4, 0, .2, 1);
    }
    .js-rv [data-rv="stagger"].rv-in > * { opacity: 1; transform: none; }
    .js-rv [data-rv="stagger"].rv-in > *:nth-child(2) { transition-delay: 70ms; }
    .js-rv [data-rv="stagger"].rv-in > *:nth-child(3) { transition-delay: 140ms; }
    .js-rv [data-rv="stagger"].rv-in > *:nth-child(4) { transition-delay: 210ms; }
    .js-rv [data-rv="stagger"].rv-in > *:nth-child(5) { transition-delay: 280ms; }
    .js-rv [data-rv="stagger"].rv-in > *:nth-child(6) { transition-delay: 350ms; }
  }


  /* ---- D4: "In production" accordion (replaces the specialities cards) ----
     One panel open at a time. Height for the tallest panel is reserved on load
     so the section below never shifts when a card opens or closes.

     Hover and open share --accent-rule; open is told apart by --accent-wash
     behind it, not by a second border colour. Both tokens are declared with the
     rest in index.html. --accent-rule spells out --accent's channels rather than
     deriving them, so changing --accent means changing that token too. */
  [data-acc] .ac-item{background:var(--raise);border:1px solid transparent;
    transition:border-color .16s ease, background .16s ease}
  [data-acc] .ac-item:hover{border-color:var(--accent-rule)}
  [data-acc] .ac-item[data-open="1"]{border-color:var(--accent-rule);background:var(--accent-wash)}
  [data-acc] .ac-head{width:100%;background:none;border:0;color:inherit;font:inherit;
    cursor:pointer;padding:16px;display:grid;grid-template-columns:minmax(0,1fr) auto;
    gap:14px;align-items:center;text-align:left;min-height:44px}
  [data-acc] .ac-head:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  [data-acc] .ac-t{font-family:var(--font-display);font-weight:var(--font-display-w);font-size:var(--acc-fs);
    line-height:var(--acc-lh);letter-spacing:var(--acc-ls);text-wrap:pretty}
  /* Hidden without JS, where the panels are all open and nothing can toggle:
     a "+" there promises an interaction the page cannot honour. */
  [data-acc] .ac-sign{display:none;font-family:var(--font-label);font-size:15px;
    color:var(--label);line-height:1;transition:color .16s ease}
  .js-rv [data-acc] .ac-sign{display:block}
  [data-acc] .ac-item[data-open="1"] .ac-sign,[data-acc] .ac-head:hover .ac-sign{color:var(--accent)}
  [data-acc] .ac-panel{overflow:hidden;transition:height .26s ease}
  /* Collapsed only when scripting is on, and set before first paint so there is
     no flash of four open panels. With JS off nothing could reopen a closed
     panel, so they stay open and the section still reads. Same reasoning, and
     the same class, as the entrance rules above. */
  .js-rv [data-acc] .ac-panel{height:0}
  [data-acc] .ac-inner{padding:0 16px 16px}
  [data-acc] .ac-src{font-family:var(--font-label);font-size:11px;
    letter-spacing:0.14em;text-transform:uppercase;color:var(--accent);margin:0 0 7px}
  [data-acc] .ac-bd{font-size:14.5px;line-height:1.62;color:var(--body);margin:0;
    max-width:54ch;text-wrap:pretty}
  @media (prefers-reduced-motion: reduce){[data-acc] .ac-panel{transition:none}}

  /* ---- C2: stress table crosshair + readout ---- */
  table[data-stress] th{font-weight:400;color:var(--label)}
  table[data-stress] th[scope="row"]{text-align:left;padding:11px 8px 11px 0;white-space:nowrap}
  table[data-stress] td[data-c]{text-align:right;padding:11px 6px;cursor:default;
    transition:outline-color .12s ease}
  table[data-stress] td[data-c]:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
  table[data-stress] th[data-lit]{color:var(--accent)}
  table[data-stress] td[data-lit]{box-shadow:inset 0 0 0 1px var(--accent)}
  @media (prefers-reduced-motion: reduce){table[data-stress] td[data-c]{transition:none}}

  /* ---- form: firm-type pills (real radios, so keyboard + SR work) ---- */
  [data-pills] input{position:absolute;opacity:0;width:1px;height:1px}
  [data-pills] span{display:inline-block;padding:9px 13px;min-height:38px;
    border:1px solid var(--rule);color:var(--body);font-family:var(--font-label);
    font-size:11.5px;letter-spacing:0.1em;text-transform:uppercase;cursor:pointer;
    transition:border-color .15s ease,color .15s ease,background .15s ease}
  [data-pills] label:hover span{border-color:var(--field);color:var(--ink)}
  [data-pills] input:checked + span{border-color:var(--accent);background:var(--accent-wash);color:var(--accent)}
  [data-pills] input:focus-visible + span{outline:2px solid var(--accent);outline-offset:2px}


  /* ---- What Monarc does: the pinned three-step stage ------------------------
     One borrower package followed from inbox to model as the reader scrolls.
     The text column says which step is live; the stage plays it. site.js maps
     scroll position inside .wm-pin to a progress value 0..1 and redraws the
     stage from it, so scrolling back plays it backwards.

     The stage is drawn at a fixed 760x520 and scaled to its box, which is why
     its insides are in px. Colours the page has no token for are declared here
     by role:
       --wm-well        the recessed ground under chips and the sheet
       --wm-shine       the bright gold of a lit figure
       --wm-lit         the ground behind a lit figure
       --wm-wash        the row highlight in the sheet
       --wm-step-idle   a step number the reader has not reached yet */
  .wm, .xr { --wm-well: #101316; --wm-shine: #d9a24a; --wm-lit: #1d1a14; --wm-wash: rgba(192,139,55,0.10); --wm-step-idle: #6a7079; --wm-ease: cubic-bezier(.25,.6,.25,1); }
  .wm .num { font-variant-numeric: tabular-nums; }

  .wm-head { padding: clamp(76px, 9vw, 140px) var(--gut) 0; }
  .wm-kick { font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); margin-bottom: 14px; }

  .wm-pin { height: 300vh; position: relative; }
  .wm-pin-in { position: sticky; top: var(--bar-off); height: calc(100vh - var(--bar-off)); display: grid; grid-template-columns: minmax(0, .72fr) minmax(0, 1.28fr); gap: clamp(28px, 4vw, 72px); align-items: center; padding: 0 var(--gut); }
  .wm-stxt { display: grid; gap: 30px; }
  .wm-sx { position: relative; padding-left: 26px; transition: opacity .5s var(--wm-ease); }
  .wm-sx::before { content: ""; position: absolute; left: 0; top: 4px; bottom: 4px; width: 1px; background: var(--rule); }
  .wm-sx .bar { position: absolute; left: 0; top: 4px; width: 1px; height: 0; background: var(--accent); }
  .wm-sx .n { font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; color: var(--wm-step-idle); margin-bottom: 10px; transition: color .4s; }
  .wm-sx h3 { font-family: var(--font-display); font-weight: var(--font-display-w); font-size: var(--card-fs); line-height: var(--card-lh); letter-spacing: var(--card-ls); margin: 0 0 10px; color: var(--label); transition: color .4s; text-wrap: balance; }
  .wm-sx p { font-size: 15.5px; line-height: 1.6; color: var(--body); margin: 0; max-height: 0; overflow: hidden; opacity: 0; transition: max-height .6s var(--wm-ease), opacity .5s; text-wrap: pretty; }
  .wm-sx.on .n { color: var(--accent); }
  .wm-sx.on h3 { color: var(--ink); }
  .wm-sx.on p { max-height: 200px; opacity: 1; }
  .wm-sx.done .n { color: var(--label); }

  .wm-swrap { position: relative; width: min(100%, calc((100vh - var(--bar-off) - 100px) * 1.4615)); aspect-ratio: 760 / 520; justify-self: end; }
  .wm-stage { position: absolute; left: 0; top: 0; width: 760px; height: 520px; transform-origin: 0 0; }
  .wm-frame { position: absolute; inset: 0; border: 1px solid var(--rule-soft); background: radial-gradient(ellipse at 70% 40%, rgba(192,139,55,.05), transparent 60%), var(--ground); }
  .wm-frame::before { content: ""; position: absolute; inset: 0; background-image: radial-gradient(var(--rule) 1px, transparent 1px); background-size: 20px 20px; opacity: .45; }
  .wm-corner { position: absolute; font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--label); }
  .wm-corner b { color: var(--accent); font-weight: 400; }

  .wm-doc { position: absolute; left: 0; top: 0; width: 132px; height: 170px; border: 1px solid var(--rule); background: var(--box); padding: 12px; box-shadow: 0 18px 40px -18px rgba(0,0,0,.8); transform-origin: 50% 50%; will-change: transform; }
  .wm-doc .t { font-family: var(--font-label); font-size: 9.5px; letter-spacing: 0.14em; color: var(--accent); margin-bottom: 6px; }
  .wm-doc b { display: block; font-family: var(--font-label); font-weight: 400; font-size: 11.5px; color: var(--ink); margin-bottom: 12px; line-height: 1.3; }
  .wm-doc i { display: block; height: 4px; background: var(--rule); margin-top: 7px; }
  .wm-doc i:nth-of-type(2n) { width: 72%; }
  .wm-doc i:nth-of-type(3n) { width: 86%; }
  .wm-doc i.hl { transition: background .4s, box-shadow .4s; }
  .wm-doc.lit i.hl { background: var(--accent); box-shadow: 0 0 12px var(--accent); }
  .wm-scan { position: absolute; left: 0; top: 0; width: 150px; height: 2px; background: var(--wm-shine); box-shadow: 0 0 18px 3px rgba(217,162,74,.5); opacity: 0; }
  .wm-scan::after { content: ""; position: absolute; left: 0; right: 0; bottom: 2px; height: 40px; background: linear-gradient(transparent, rgba(217,162,74,.14)); }

  .wm-gate { position: absolute; top: 96px; height: 330px; width: 1px; background: var(--rule); opacity: 0; }
  .wm-gate::before { content: ""; position: absolute; inset: 0; background: var(--accent); box-shadow: 0 0 22px 2px var(--accent); opacity: 0; transition: opacity .25s; }
  .wm-gate.lit::before { opacity: 1; }
  .wm-gate span { position: absolute; left: 50%; transform: translateX(-50%); white-space: nowrap; font-family: var(--font-label); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--label); transition: color .25s; }
  .wm-gate.lit span { color: var(--accent); }
  .wm-gate .top { top: -26px; }
  .wm-gate .bot { bottom: -26px; }

  .wm-chip { position: absolute; left: 0; top: 0; width: 184px; height: 38px; display: flex; justify-content: space-between; align-items: center; padding: 0 12px; border: 1px solid var(--rule); background: var(--wm-well); font-family: var(--font-label); font-size: 12.5px; opacity: 0; will-change: transform, opacity; transition: border-color .4s, background .4s, box-shadow .4s; }
  .wm-chip .k { color: var(--label); font-size: 11px; letter-spacing: .04em; transition: color .4s; }
  .wm-chip .v { color: var(--ink); transition: color .4s; }
  .wm-chip.gold { border-color: var(--accent); background: var(--wm-lit); box-shadow: 0 0 0 4px rgba(192,139,55,.07); }
  .wm-chip.gold .k, .wm-chip.gold .v { color: var(--wm-shine); }
  .wm-chip.flash { animation: wm-flash .6s var(--wm-ease); }
  @keyframes wm-flash { 0% { box-shadow: 0 0 0 0 rgba(217,162,74,.8); } 100% { box-shadow: 0 0 0 14px rgba(217,162,74,0); } }

  .wm-sheet { position: absolute; left: 486px; top: 150px; width: 250px; border: 1px solid var(--rule); background: var(--wm-well); opacity: 0; font-family: var(--font-label); font-size: 12.5px; }
  .wm-sheet .hd { height: 36px; display: flex; align-items: center; justify-content: space-between; padding: 0 12px; border-bottom: 1px solid var(--rule); font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--label); }
  .wm-sheet .row { height: 44px; display: grid; grid-template-columns: 44px 1fr auto; align-items: center; border-bottom: 1px solid var(--rule-soft); transition: background .4s; }
  .wm-sheet .row:last-child { border-bottom: 0; }
  .wm-sheet .row span { padding: 0 10px; }
  .wm-sheet .row span:first-child { color: var(--wm-step-idle); border-right: 1px solid var(--rule-soft); height: 100%; display: grid; place-items: center; font-size: 11px; }
  .wm-sheet .row span:nth-child(2) { color: var(--body); }
  .wm-sheet .row .cv { color: var(--ink); opacity: 0; }
  .wm-sheet .row.hot { background: var(--wm-wash); }
  .wm-sheet .row.hot .cv { color: var(--wm-shine); box-shadow: inset 0 0 0 1px var(--accent); padding: 6px 10px; margin-right: 6px; }
  .wm-thread { position: absolute; inset: 0; width: 760px; height: 520px; pointer-events: none; overflow: visible; }
  .wm-thread path { fill: none; stroke: var(--accent); stroke-width: 1.5; filter: drop-shadow(0 0 5px rgba(217,162,74,.7)); }
  .wm-thread circle { fill: var(--wm-shine); }
  .wm-tag { position: absolute; left: 200px; top: 410px; padding: 10px 13px; border: 1px solid var(--accent); background: var(--wm-lit); font-family: var(--font-label); font-size: 11.5px; color: var(--wm-shine); opacity: 0; white-space: nowrap; }
  .wm-tag small { display: block; font-size: 9.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--label); margin-bottom: 4px; }

  .wm-more { display: inline-block; font-family: var(--font-label); font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); border-bottom: 1px solid var(--accent-rule); padding-bottom: 4px; }
  .wm-more:hover { border-bottom-color: var(--accent); }
  .wm-tail { padding: 0 var(--gut) clamp(70px, 8.4vw, 128px); }

  /* Narrow: the stage goes under the text, and the three steps share one grid
     cell so only the live one shows. The pin stays, one screen tall. */
  @media (max-width: 900px) {
    .wm-pin-in { grid-template-columns: minmax(0, 1fr); align-content: center; gap: 22px; }
    /* site.js moves a camera over the stage here, so the box clips what is
       out of frame and carries the border the stage's own frame loses. The
       gates are shortened so both their labels fit in the close-up. */
    .wm-swrap { width: 100%; justify-self: stretch; overflow: hidden; border: 1px solid var(--rule-soft); background: var(--ground); }
    .wm-gate { top: 136px; height: 236px; }
    .wm-sx { grid-area: 1 / 1; opacity: 0; }
    .wm-sx.on { opacity: 1; }
  }
  /* No motion: no pin. The stage is drawn once at its last frame and every
     step is open, so the section reads top to bottom as plain content. */
  @media (prefers-reduced-motion: reduce) {
    .wm *, .wm *::before, .wm *::after { animation: none !important; transition: none !important; }
    .wm-pin { height: auto; }
    .wm-pin-in { position: relative; top: 0; height: auto; padding-top: 40px; padding-bottom: 40px; }
    .wm-sx { opacity: 1 !important; grid-area: auto !important; }
    .wm-sx p { max-height: none; opacity: 1; }
  }


  /* ---- The anatomy of one number (product.html) ----------------------------
     The home page's three steps again, seen as three layers of one figure
     rather than as a sequence: your model, the restatement, the source. The layers
     stand upright and run left to right, and a beam passes through the same
     point on each. Colour roles are the --wm-* set declared on .wm above.

     .xr-rig holds the three planes in its own 3D space, spaced along its Z
     axis; its transform (set by site.js, which also adds the mouse tilt)
     turns that axis to point right, so the stack reads left to right. */
  .xr-kick { font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); margin-bottom: 14px; }
  .xr-scene { position: relative; height: clamp(360px, 38vw, 580px); perspective: 2000px; perspective-origin: 50% 40%; margin-top: clamp(20px, 3vw, 40px);
    background: radial-gradient(ellipse 60% 55% at 50% 50%, rgba(192,139,55,.07), transparent 70%); }
  .xr-scale { position: absolute; left: 50%; top: 48%; width: 0; height: 0; transform-style: preserve-3d; transform: scale(var(--k, 1)); }
  .xr-rig { position: absolute; left: -190px; top: -130px; width: 380px; height: 260px; transform-style: preserve-3d; }
  .xr-plane { position: absolute; inset: 0; border: 1px solid var(--rule); background: rgba(21,26,30,.86); transform: translateZ(var(--xr-gap, 480px)); transform-style: preserve-3d;
    transition: transform 1.3s var(--wm-ease), border-color .5s, opacity .5s, box-shadow .5s, background .5s; }
  .xr-plane::before { content: ""; position: absolute; inset: 0; background-image: radial-gradient(var(--rule-soft) 1px, transparent 1px); background-size: 16px 16px; opacity: .6; }
  .ex .xr-plane.p0 { transform: translateZ(calc(var(--xr-gap, 480px) * 2)); }
  .ex .xr-plane.p1 { transform: translateZ(var(--xr-gap, 480px)); }
  .ex .xr-plane.p2 { transform: translateZ(0); }
  .ex .xr-plane { opacity: .6; }
  .ex .xr-plane.on { opacity: 1; border-color: var(--accent); background: rgba(29,26,20,.94); box-shadow: 0 0 60px rgba(192,139,55,.22); }
  .xr-pl { position: absolute; left: 0; top: -26px; font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--label); white-space: nowrap; transition: color .4s; }
  .xr-plane.on .xr-pl { color: var(--accent); }
  .xr-anc { position: absolute; left: 245px; top: 145px; width: 10px; height: 10px; border-radius: 50%; background: var(--wm-shine); box-shadow: 0 0 12px var(--wm-shine); opacity: 0; transition: opacity .5s 1.6s; }
  .xr-anc::after { content: ""; position: absolute; inset: -8px; border-radius: 50%; border: 1px solid var(--accent); animation: xr-ring 2.2s var(--wm-ease) infinite; }
  @keyframes xr-ring { 0% { transform: scale(.4); opacity: 1; } 100% { transform: scale(1.8); opacity: 0; } }
  .ex .xr-anc { opacity: 1; }
  .xr-beam { position: absolute; left: 249px; top: 150px; width: 2px; height: calc(var(--xr-gap, 480px) * 2); transform-origin: 50% 0; transform: translateZ(calc(var(--xr-gap, 480px) * 2)) rotateX(-90deg) scaleY(0);
    background: linear-gradient(0deg, rgba(217,162,74,.25), var(--wm-shine) 50%, rgba(217,162,74,.25)); background-size: 100% 200%;
    box-shadow: 0 0 14px 2px rgba(217,162,74,.55); transition: transform 1.2s var(--wm-ease) 1.2s; }
  .ex .xr-beam { transform: translateZ(calc(var(--xr-gap, 480px) * 2)) rotateX(-90deg) scaleY(1); animation: xr-pulse 2.4s linear infinite; }
  .xr-beam.b2 { transform: translateZ(calc(var(--xr-gap, 480px) * 2)) rotateX(-90deg) rotateY(90deg) scaleY(0); }
  .ex .xr-beam.b2 { transform: translateZ(calc(var(--xr-gap, 480px) * 2)) rotateX(-90deg) rotateY(90deg) scaleY(1); }
  @keyframes xr-pulse { from { background-position: 0 0; } to { background-position: 0 -200%; } }

  /* what is drawn on each plane, in the plane's own 380x260 px */
  .xr-doc { position: absolute; width: 92px; height: 120px; border: 1px solid var(--rule); background: var(--box); padding: 9px; font-family: var(--font-label); }
  .xr-doc .t { font-size: 8.5px; letter-spacing: .12em; color: var(--accent); }
  .xr-doc b { display: block; font-weight: 400; font-size: 9.5px; color: var(--ink); margin: 4px 0 8px; line-height: 1.25; }
  .xr-doc i { display: block; height: 3px; background: var(--rule); margin-top: 6px; }
  .xr-doc i:nth-of-type(2n) { width: 70%; }
  .xr-doc.main { left: 200px; top: 62px; width: 112px; height: 150px; }
  .xr-doc.main i.hl { background: var(--accent); box-shadow: 0 0 8px var(--accent); }
  .xr-brg { position: absolute; left: 26px; top: 26px; width: 300px; font-family: var(--font-label); font-size: 11px; }
  .xr-brg div { display: flex; justify-content: space-between; height: 28px; align-items: center; color: var(--body); border-bottom: 1px solid var(--rule-soft); }
  .xr-brg div.tot { color: var(--ink); }
  .xr-brg div.house { height: 40px; margin-top: 10px; border: 1px solid var(--accent); padding: 0 10px; color: var(--wm-shine); background: var(--wm-wash); }
  .xr-grid { position: absolute; left: 90px; top: 30px; display: grid; grid-template-columns: 30px 80px 80px 80px; grid-auto-rows: 28px; font-family: var(--font-label); font-size: 10.5px; border-top: 1px solid var(--rule); border-left: 1px solid var(--rule); }
  .xr-grid span { border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); display: flex; align-items: center; padding: 0 7px; color: var(--label); white-space: nowrap; overflow: hidden; }
  .xr-grid span.h { color: var(--wm-step-idle); justify-content: center; font-size: 9.5px; }
  .xr-grid span.v { color: var(--ink); justify-content: flex-end; }
  .xr-grid span.hot { background: var(--wm-wash); color: var(--wm-shine); box-shadow: inset 0 0 0 1px var(--accent); }
  .xr-cap { text-align: center; font-family: var(--font-label); font-size: 11.5px; letter-spacing: .06em; color: var(--label); min-height: 18px; margin-top: 6px; }
  .xr-cap b { font-weight: 400; color: var(--accent); }

  /* the three steps, one under each plane. A running bar along the top edge
     shows when the next one will come up; pressing any of them stops that. */
  .xr-tabs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(20px, 2.4vw, 34px); margin-top: clamp(24px, 3vw, 40px); }
  .xr-tab { position: relative; display: block; width: 100%; text-align: left; background: none; border: 0; border-top: 1px solid var(--rule); padding: 20px 0 0; cursor: pointer; color: inherit; font: inherit; }
  .xr-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; }
  .xr-tab .pb { position: absolute; left: 0; top: -1px; height: 1px; width: 0; background: var(--accent); }
  .xr-tab.on .pb { width: 100%; }
  .cycling .xr-tab.on .pb { width: 0; animation: xr-fill var(--xr-cyc, 5.2s) linear forwards; }
  @keyframes xr-fill { to { width: 100%; } }
  .xr-tab .n { font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; color: var(--wm-step-idle); margin-bottom: 12px; transition: color .4s; }
  .xr-tab h3 { font-family: var(--font-display); font-weight: var(--font-display-w); font-size: var(--acc-fs); line-height: var(--acc-lh); letter-spacing: var(--acc-ls); margin: 0 0 10px; color: var(--label); transition: color .4s; text-wrap: balance; }
  .xr-tab p { font-size: 14.5px; line-height: 1.55; color: var(--wm-step-idle); margin: 0; transition: color .4s; text-wrap: pretty; }
  .xr-tab.on .n { color: var(--accent); }
  .xr-tab.on h3, .xr-tab:hover h3 { color: var(--ink); }
  .xr-tab.on p { color: var(--body); }

  /* Phone: the stack stays 3D, but site.js moves a camera onto one plane at
     a time (the live step's) and zooms it to the column, so its text is
     readable. The scene clips the neighbours. As on the home page, the
     three steps share one grid cell right under the scene, so only the live
     one shows and the cell holds the tallest one's height; tapping it moves
     on to the next (site.js). */
  @media (max-width: 760px) {
    .xr-pin { height: 300vh; position: relative; }
    .xr-pin-in { position: sticky; top: var(--bar-off); height: calc(100vh - var(--bar-off)); display: flex; flex-direction: column; justify-content: center; }
    .xr-scene { height: 360px; overflow: hidden; perspective-origin: 50% 48%; }
    .xr-scale { transition: transform .9s var(--wm-ease); }
    .xr-now .xr-scale, .xr-now .xr-plane, .xr-now .xr-beam, .xr-now .xr-anc { transition: none !important; }
    .xr-tabs { grid-template-columns: minmax(0, 1fr); gap: 0; }
    .xr-tab { grid-area: 1 / 1; opacity: 0; visibility: hidden; transition: opacity .4s, visibility 0s .4s; }
    .xr-tab.on { opacity: 1; visibility: visible; transition: opacity .4s; }
  }
  @media (prefers-reduced-motion: reduce) {
    .xr *, .xr *::before, .xr *::after { animation: none !important; transition: none !important; }
    .xr-pin { height: auto; }
    .xr-pin-in { position: relative; top: 0; height: auto; }
  }


  /* ---- Hero: the typewriter line and the still photograph -----------------
     site.js types into [data-r="herotyped"]; the caret takes no width of its
     own (its right margin cancels it), so "runs on." never shifts when it
     comes and goes. The pencil stroke under the finished subject is placed by script.
     Above 860px the line holds on one row and may run past the copy column
     into the photograph; below, it wraps, and keeps two rows' height so the
     copy underneath does not jump as subjects of different lengths are typed.
     The photograph's movement is script, in site.js ("Hero photograph"). */
  [data-r="herocaret"] { display: inline-block; width: 3px; height: 0.78em; margin: 0 -3px 0 0.04em; vertical-align: -0.04em; background: var(--accent); opacity: 0; transition: opacity .25s ease; }
  [data-r="herocaret"].on { opacity: 1; animation: hero-caret 1s steps(2, jump-none) infinite; }
  [data-r="herocaret"].on.typing { animation: none; }
  @keyframes hero-caret { 50% { opacity: 0; } }
  /* the underline is a scribble, not a rule: one curved brush mark of uneven
     width, arched: both ends low, the middle lifted toward the word, revealed along
     its centre line by a dashed mask so it draws on the way a hand would.
     Edges roughened by the noise filter in the markup. */
  [data-r="herorule"] { position: absolute; left: 0; top: 0; width: 0; height: 0.5em; overflow: visible; pointer-events: none; }
  [data-r="herorule"] g > path { fill: var(--accent); }
  [data-r="herorule"] mask path { fill: none; stroke: #fff; stroke-width: 16; stroke-linecap: butt; stroke-dasharray: 1 2; stroke-dashoffset: 1; }
  [data-r="herorule"].drawn mask path { stroke-dashoffset: 0; }
  @media (prefers-reduced-motion: no-preference) {
    [data-r="herorule"].drawn #hero-u1 path { transition: stroke-dashoffset .38s cubic-bezier(.5,0,.3,1); }
  }
  @media (min-width: 861px) { [data-r="heroline"] { white-space: nowrap; } }
  @media (max-width: 860px) { [data-r="herolower"] { grid-template-columns: minmax(0, 1fr) !important; } }
  @media (max-width: 860px) { .js-rv [data-r="heroline"] { min-height: calc(2em * var(--hero-lh)); } }
  [data-r="herowrap"] img { transform-origin: 58% 45%; }

  /* ---- Hero: buttons and phone ------------------------------------------- */
  /* the global a:hover tints links gold, which on the gold button would make
     its label vanish; the buttons take their own hover instead */
  [data-r="herocta"] a { transition: background-color .16s ease, border-color .16s ease, color .16s ease; }
  [data-r="herocta"] a:first-child:hover { color: var(--on-accent); background: #d39b45; }
  [data-r="herocta"] a:last-child:hover { color: var(--ink); border-color: var(--accent); }
  @media (max-width: 700px) {
    [data-r="heroeyebrow"] { letter-spacing: 0.14em !important; }
    /* two equal buttons that share the row, and stack once it is too narrow */
    [data-r="herocta"] a { flex: 1 1 180px; }
    [data-r="herocta"] { justify-content: flex-start !important; }
  }

  /* ---- One record: the pinned three-card track (index.html, #record) -----
     The track is taller than the screen and .or-pin sticks under the header
     while it scrolls past; site.js turns scroll through the track into which
     cards are showing (one, two, three). It pins wider than the 1000px split
     and at least 600px tall. Below 760px tall the cards pack tighter so the
     whole panel still fits under the header; from 760px up they keep the full
     spacing. Shorter than 600px or narrower than
     that, it is a plain section with every card open. With reduced motion it
     still pins, and the cards appear without the fade. */
  .or-pin { padding: clamp(76px, 9vw, 140px) var(--gut) clamp(70px, 8.4vw, 128px); }
  .or-split { width: 100%; }
  .or-flow { display: grid; gap: 6px; }
  .or-fr { position: relative; border: 1px solid var(--rule-soft); background: var(--raise); padding: 20px 22px; }
  /* the record card is drawn in the brass: a solid --accent edge, and a fill of
     --accent mixed 11% into the darkest ground, a deep warm brown */
  .or-fr.or-mid { border-color: var(--accent); background: #231f17; background: color-mix(in srgb, var(--accent) 11%, #101316); z-index: 1; }
  .or-k { font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--label); margin-bottom: 10px; }
  .or-mid .or-k { color: var(--accent); }
  .or-fr h3 { font-family: var(--font-display); font-weight: var(--font-display-w); font-size: var(--acc-fs); line-height: var(--acc-lh); letter-spacing: var(--acc-ls); margin: 0 0 8px; }
  .or-fr p { font-size: 14.5px; line-height: 1.56; color: var(--body); margin: 0; text-wrap: pretty; }
  .or-steps { margin-top: 14px; border-top: 1px solid rgba(192,139,55,0.28); }
  .or-s { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px; align-items: baseline; padding: 13px 0; border-bottom: 1px solid rgba(192,139,55,0.18); }
  .or-s:last-child { border-bottom: 0; padding-bottom: 0; }
  .or-v { min-width: 74px; font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); white-space: nowrap; }
  .or-x { font-size: 14.5px; line-height: 1.55; color: var(--body); }
  .or-x em { font-style: normal; color: var(--accent); }
  .or-prog { display: none; gap: 7px; margin-top: 22px; }
  .or-prog i { width: 26px; height: 2px; background: var(--rule); transition: background .3s ease; }
  .or-prog i.on { background: var(--accent); }

  @media (min-width: 1001px) and (min-height: 600px) {
    .or-track { position: relative; height: 290vh; }
    .or-pin { position: sticky; top: var(--bar-off); min-height: calc(100vh - var(--bar-off)); display: flex; align-items: center; padding-top: clamp(24px, 6vh, 80px); padding-bottom: clamp(20px, 4vh, 56px); }
    .or-prog { display: flex; }
    /* hidden only when scripting is on, so the cards never go missing without it */
    .js-rv .or-stage { opacity: 0; }
    .js-rv .or-stage.on { opacity: 1; }
  }
  @media (min-width: 1001px) and (min-height: 600px) and (prefers-reduced-motion: no-preference) {
    .js-rv .or-stage { transform: translateY(26px); transition: opacity .5s cubic-bezier(.22,.7,.3,1), transform .5s cubic-bezier(.22,.7,.3,1); }
    .js-rv .or-stage.on { transform: none; }
  }
  /* a short laptop window: the same panel, packed tighter so it fits */
  @media (min-width: 1001px) and (min-height: 600px) and (max-height: 759px) {
    .or-fr { padding: 13px 18px 14px; }
    .or-k { margin-bottom: 6px; }
    .or-fr h3 { margin-bottom: 4px; }
    .or-steps { margin-top: 8px; }
    .or-s { padding: 7px 0; }
  }
  @media (max-width: 700px) {
    .or-fr { padding: 18px 18px; }
    .or-s { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  }

  /* ---- How Monarc is different: the pinned list of seven (index.html, #principles)
     Built on the same terms as One record above it. Wider than 1000px and at
     least 600px tall, with scripting on, .df-track is a scroll track and
     .df-pin sticks under the header: the seven headlines stay listed on the
     left and site.js opens one at a time on the right as the track scrolls
     past. Anywhere else, and without scripting, the list is dropped and all
     seven stand open in order, so nothing depends on the pin. The list
     borrows the product steps' grammar: an idle number in --wm-step-idle that
     turns brass, a headline that goes from --label to --ink. */
  .df { padding: 0 var(--gut) clamp(70px, 8.4vw, 128px); scroll-margin-top: var(--bar-off); }
  .df-pin { padding: clamp(48px, 5vw, 72px) 0 clamp(26px, 3vw, 38px); }
  .df-chip { display: inline-block; padding: 8px 14px; border: 1px solid var(--accent); font-family: var(--font-label); font-size: clamp(11px, 1vw, 12px); letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); margin-bottom: clamp(18px, 2vw, 26px); }
  .df-list { display: none; list-style: none; margin: 0; padding: 0; }
  .df-list button { display: grid; grid-template-columns: 38px minmax(0, 1fr); align-items: baseline; width: 100%; padding: 13px 0 14px; background: none; border: 0; border-top: 1px solid var(--rule-soft); text-align: left; font: inherit; color: inherit; cursor: pointer; }
  .df-list li:last-child button { border-bottom: 1px solid var(--rule-soft); }
  .df-list .n { font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; color: var(--wm-step-idle, #6a7079); transition: color .4s; }
  .df-list .t { font-family: var(--font-display); font-weight: var(--font-display-w); font-size: var(--acc-fs); line-height: var(--acc-lh); letter-spacing: var(--acc-ls); color: var(--label); transition: color .4s; text-wrap: balance; }
  .df-list button:hover .t { color: var(--body); }
  .df-list button[aria-current="true"] { border-top-color: var(--accent); }
  .df-list button[aria-current="true"] .n { color: var(--accent); }
  .df-list button[aria-current="true"] .t { color: var(--ink); }
  .df-p { padding: 26px 0 30px; border-top: 1px solid var(--rule-soft); }
  .df-p .n { font-family: var(--font-label); font-size: 11px; letter-spacing: 0.16em; color: var(--accent); margin-bottom: 14px; }
  .df-p h3 { font-family: var(--font-display); font-weight: var(--font-display-w); font-size: var(--card-fs); line-height: var(--card-lh); letter-spacing: var(--card-ls); margin: 0 0 12px; max-width: var(--card-mw); text-wrap: balance; }
  .df-p p { font-size: 16.5px; line-height: 1.65; color: var(--body); margin: 0; max-width: 54ch; text-wrap: pretty; }
  .df .wm-more { margin-top: clamp(32px, 3.4vw, 48px); }

  @media (min-width: 1001px) and (min-height: 600px) {
    .js-rv .df-track { position: relative; height: 420vh; }
    .js-rv .df-pin { position: sticky; top: var(--bar-off); min-height: calc(100vh - var(--bar-off)); display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: clamp(40px, 6vw, 110px); align-items: center; padding: clamp(24px, 6vh, 72px) 0; }
    .js-rv .df-list { display: block; }
    .js-rv .df-stage { display: grid; }
    /* the open item: the headline at the size of the What's at stake panels */
    .js-rv .df-p { grid-area: 1 / 1; padding: 0; border-top: 0; opacity: 0; visibility: hidden; }
    .js-rv .df-p.on { opacity: 1; visibility: visible; }
    .js-rv .df-p h3 { font-size: var(--ctx-fs); line-height: var(--ctx-lh); letter-spacing: var(--ctx-ls); max-width: var(--ctx-mw); margin-bottom: 22px; }
    /* under whichever item is open: the items share one cell, so the link holds still */
    .js-rv .df-stage .wm-more { grid-row: 2; justify-self: start; }
  }
  @media (min-width: 1001px) and (min-height: 600px) and (prefers-reduced-motion: no-preference) {
    .js-rv .df-p { transform: translateY(18px); transition: opacity .45s cubic-bezier(.22,.7,.3,1), transform .45s cubic-bezier(.22,.7,.3,1), visibility 0s linear .45s; }
    .js-rv .df-p.on { transform: none; transition-delay: 0s; }
  }
  /* a short laptop window: the list packs tighter so all seven fit beside the panel */
  @media (min-width: 1001px) and (min-height: 600px) and (max-height: 759px) {
    .js-rv .df-list button { padding: 9px 0 10px; }
    .js-rv .df-chip { margin-bottom: 12px; }
  }
