/* ============================================================
   TURNING OVER LTD
   Built on Avenir™ - clarity, trust, substance.

   Two rules from the build brief survive the change of design
   system, because they are about behaviour rather than looks:

   - One primary action per screen. Here that is the Solar Accent
     button, and it says "request a demonstration". Avenir reserves
     Solar for the accent variant precisely because it appears
     sparingly, which is the same discipline arrived at from the
     other direction.
   - Status is never carried by colour alone. Availability is a word
     first; the tag treatment agrees with the word rather than
     replacing it. Avenir's state colours are not borrowed for it,
     because availability is not an error, a warning or a success.
   ============================================================ */

/* ---------- fonts: self-hosted -----------------------------------
   Nothing on this site is fetched from a third party, fonts
   included. A site whose whole proposition is that no data leaves
   your organisation should not be phoning a font CDN on every page
   load, and public-sector networks block them often enough to
   matter. Latin and latin-extended subsets only. */
@font-face{font-family:"Lora";font-style:normal;font-weight:400 700;font-display:swap;
  src:url("/assets/fonts/lora-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Lora";font-style:normal;font-weight:400 700;font-display:swap;
  src:url("/assets/fonts/lora-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Lora";font-style:italic;font-weight:400 700;font-display:swap;
  src:url("/assets/fonts/lora-italic-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Lora";font-style:italic;font-weight:400 700;font-display:swap;
  src:url("/assets/fonts/lora-italic-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Inter Tight";font-style:normal;font-weight:400 500;font-display:swap;
  src:url("/assets/fonts/inter-tight-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Inter Tight";font-style:normal;font-weight:400 500;font-display:swap;
  src:url("/assets/fonts/inter-tight-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
/* Fragment Mono ships one weight. The 400 file answers to 500 as well,
   so the label style can ask for medium without the browser faking a
   bold that would thicken the tracked caps. */
@font-face{font-family:"Fragment Mono";font-style:normal;font-weight:400 500;font-display:swap;
  src:url("/assets/fonts/fragment-mono-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Fragment Mono";font-style:normal;font-weight:400 500;font-display:swap;
  src:url("/assets/fonts/fragment-mono-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}

/* ============================================================
   TOKENS - Avenir
   ============================================================ */
:root{
  /* colours */
  --color-deep-navy:#0b2e3c;
  --color-mist-blue:#b8cdd4;
  --color-dusk-stone:#e5e3e1;
  --color-linen-lift:#ffffff;
  --color-slate-teal:#0b2e3c;
  --color-blush-error:#d16c6c;
  --color-fern-success:#6fbf8a;
  --color-solar-accent:#ffe96f;
  /* Signal Mint - the client's action colour, and the only place any
     accent appears. Deep navy on it measures 7.08:1, so the label is
     always navy: white or warm parchment on this green comes out at
     about 2:1 and fails badly, which is the one way to get it wrong.
     The dark step is the hover, six percent down in lightness, still
     5.37:1 with navy. */
  --color-signal-mint:#1acf97;
  --color-signal-mint-dark:#15b483;
  /* The arrow on every call to action. A mask rather than a character:
     the subset fonts carry no U+2192, so a literal arrow would fall
     back to whatever the operating system offers and change shape from
     machine to machine. */
  --icon-arrow:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 8h11M9 4l4 4-4 4' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --color-amber-warning:#c9a24d;
  --color-warm-parchment:#f7f6f5;

  /* surfaces */
  --surface-warm-parchment:#f7f6f5;
  --surface-linen-lift:#ffffff;
  --surface-dusk-stone:#e5e3e1;
  --surface-navy-veil:rgba(11,46,60,.08);
  /* The boundary of a form control is a non-text component, and SC
     1.4.11 asks for 3:1 against what surrounds it. --rule is a
     divider between blocks of text and nowhere near that; a field
     drawn with it looks tidy and tells a low-vision user nothing about
     where the box is. 3.58:1 on white. */
  --rule-input:#7a8a90;
  --surface-slate-teal:#0b2e3c;

  /* derived, for contrast only - see the note below */
  --ink:var(--color-deep-navy);
  --ink-muted:#46606a;          /* deep navy at 75% over parchment: 6.2:1 */
  --ink-reverse:var(--color-warm-parchment);
  --ink-reverse-muted:#c8ced0;  /* parchment at 80% over navy: 9.0:1 */
  --error-ink:#b33838;          /* blush error darkened to 5.9:1 on white */
  --rule:rgba(11,46,60,.14);
  --rule-reverse:rgba(247,246,245,.22);

  /* typography */
  --font-lora-editorial:"Lora",Georgia,"Iowan Old Style",serif;
  --font-inter-tight-system:"Inter Tight",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --font-fragment-mono-label:"Fragment Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;

  --text-caption:12px;   --leading-caption:1;    --tracking-caption:.063em;
  --text-body-sm:14px;   --leading-body-sm:1.43; --tracking-body-sm:.018em;
  --text-body:16px;      --leading-body:1.5;
  --text-body-lg:24px;   --leading-body-lg:1.17;
  /* One step Avenir does not define. Its scale jumps 16px straight to
     24px, and 24px at 1.17 is calibrated for a one-line statement -
     used on a five-line standfirst it sets as a slab that competes
     with the headline. This is for ledes only; every heading that
     uses body-lg keeps it. */
  --text-lede:20px;      --leading-lede:1.45;
  --text-heading-sm:24px;--leading-heading-sm:1.17;--tracking-heading-sm:-.063em;
  --text-heading:32px;   --leading-heading:1.13; --tracking-heading:-.023em;
  --text-heading-lg:40px;--leading-heading-lg:1.1;--tracking-heading-lg:-.05em;
  --text-display-sm:52px;--leading-display-sm:1.15;--tracking-display-sm:-.058em;
  --text-display:64px;   --leading-display:1.06; --tracking-display:-.047em;
  --text-display-lg:88px;--leading-display-lg:1;  --tracking-display-lg:-.023em;

  --font-weight-regular:400;
  --font-weight-medium:500;

  /* spacing */
  --spacing-4:4px;   --spacing-6:6px;   --spacing-8:8px;   --spacing-12:12px;
  --spacing-16:16px; --spacing-20:20px; --spacing-24:24px; --spacing-32:32px;
  --spacing-40:40px; --spacing-48:48px; --spacing-64:64px; --spacing-80:80px;
  --spacing-120:120px; --spacing-160:160px; --spacing-200:200px;

  /* layout */
  --page-max-width:1800px;
  --section-gap:120px;
  --card-padding:24px;
  --element-gap:16px;
  /* Avenir specifies 32px, which reads tight on a wide screen for a
     system whose whole character is breathing room. Stepped down
     through the scale at each of its breakpoints instead, never below
     24px. */
  --page-padding:var(--spacing-64);
  --header-h:68px;
  --measure:68ch;               /* reading measure inside an 1800px page */
  /* Display type needs a much tighter measure than prose. Without one,
     a section heading on a wide screen sets as a single enormous line
     while the standfirst under it wraps at 44ch, and the two stop
     looking like they belong to each other. */
  --measure-heading:26ch;

  /* radius */
  --radius-tags:8px;  --radius-cards:12px; --radius-large:20px;
  --radius-small:8px; --radius-circle:9999px;
  --radius-inputs:8px; --radius-buttons:8px;

  /* shadow */
  --shadow-nav-resting:0 4px 10px 0 rgba(11,46,60,.08);

  /* motion */
  --duration-card-hover:200ms; --easing-card-hover:ease;
  --duration-ui-transition:300ms; --easing-ui-transition:linear;
  /* Linear is right for a colour fade and wrong for anything that
     moves: a panel sliding open at a constant rate reads as mechanical.
     One easing token for the things that travel. */
  --duration-reveal:340ms; --easing-reveal:cubic-bezier(.32,.72,0,1);
}

/* Solar Accent is no longer the action colour - Signal Mint is. Solar
   now marks the bracketed placeholders that stand in for the company
   number and registered office, which is build chrome and disappears
   at launch. Keeping the marker a different colour from the call to
   action is deliberate: an unresolved placeholder should never be
   mistakable for something to click. */

/* A note on the derived colours above. Avenir's ten colours are used
   exactly as specified. The four --ink-* values are not new colours:
   each is deep navy or warm parchment blended with the surface it sits
   on, so secondary text stays in the palette and still clears 4.5:1.
   --error-ink is Blush Error darkened for text use only; Blush Error
   itself measures 3.5:1 on white, which the design system flags as a
   contrast risk and which fails AA for body text. The functional
   colour still draws the field border; the darker one carries the
   words. */

/* ============================================================
   BASE
   ============================================================ */
*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none !important}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}
  /* ::details-content is not matched by *, ::before or ::after, so the
     accordion kept sliding open while everything else stopped. */
  *::details-content{transition-duration:.01ms !important}
}
/* Selected text takes the brand navy with white on it, rather than the
   browser's default blue. 13.2:1, so a selected line is still the
   easiest thing on the page to read. */
::selection{background:var(--color-deep-navy);color:var(--color-warm-parchment)}
::-moz-selection{background:var(--color-deep-navy);color:var(--color-warm-parchment)}

body{
  margin:0;
  background:var(--surface-warm-parchment);
  color:var(--ink);
  font-family:var(--font-inter-tight-system);
  font-size:var(--text-body);
  line-height:var(--leading-body);
  font-weight:var(--font-weight-regular);
  -webkit-font-smoothing:antialiased;
  overflow-wrap:break-word;
}

/* Lora at 400 for every heading. Never bold - the light-to-normal
   serif weight is the editorial voice, and the tight negative
   tracking is what stops large text feeling loose. */
h1,h2,h3,h4,h5,h6{
  font-family:var(--font-lora-editorial);
  overflow-wrap:break-word;
  /* Even line lengths, decided by the browser rather than by where the
     measure happens to fall. Copy changes without the rag falling
     apart. */
  text-wrap:balance;
  font-weight:var(--font-weight-regular);
  color:var(--ink);
  margin:0 0 var(--spacing-16);
}
h1{font-size:clamp(var(--text-heading),2rem + 2.4vw,var(--text-display));line-height:var(--leading-display);letter-spacing:var(--tracking-display)}
h2{font-size:clamp(var(--text-heading),1.6rem + 1vw,var(--text-heading-lg));line-height:var(--leading-heading-lg);letter-spacing:var(--tracking-heading-lg)}
h3{font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm)}
h4,h5,h6{font-size:var(--text-body-lg);line-height:var(--leading-body-lg);letter-spacing:-.02em}

p{margin:0 0 var(--spacing-20)}
/* Medium, not bold. Heavy weight destroys the editorial lightness. */
strong,b{font-weight:var(--font-weight-medium)}
a{color:var(--ink);text-underline-offset:3px;text-decoration-thickness:1px;transition:color var(--duration-ui-transition) var(--easing-ui-transition)}
a:hover{color:var(--ink-muted)}
img,svg{max-width:100%}
hr{border:0;border-top:1px solid var(--rule);margin:var(--spacing-48) 0}

:focus-visible{outline:3px solid var(--color-deep-navy);outline-offset:3px;border-radius:var(--radius-small)}

/* ============================================================
   LAYOUT
   ============================================================ */
.wrap{max-width:var(--page-max-width);margin:0 auto;padding:0 var(--page-padding)}
/* Section-level headings only - a heading sitting directly in a
   full-width container. Headings inside cards, stage rows and centred
   columns are already constrained by their own parent. */
.wrap > h1,.wrap > h2,.wrap > h3{max-width:var(--measure-heading)}
.narrow{max-width:var(--measure)}
/* Text-led pages centre their column. On a page whose whole body is one
   measured column, holding it against the left gutter of an 1800px page
   leaves half the screen empty and the reading column marooned. Pages
   with card grids or two-column layouts stay at the gutter, because
   there the full width is doing work.

   The column centres; the text inside it does not. Centre-aligned
   running prose is markedly harder to read - every line starts in a
   different place, so the eye has to hunt for each one. */
.page--centred .pagehead > .narrow,
.page--centred .band > .wrap > .narrow,
.page--centred .band > .wrap > .wide,
.page--centred .band--flush > .wrap > .narrow{margin-inline:auto}
/* One column for the whole page, so the head and the body share an
   edge. The wider head measure and the wider FAQ measure both exist to
   stop a column looking stranded when it is held against the left
   gutter; centred, neither is needed, and two centred columns of
   different widths read as a misalignment. */
.page--centred .pagehead > .narrow,
.page--centred .wide{max-width:var(--measure)}
/* A little wider than the reading measure: for stacked rows such as the
   FAQ, where a 68ch column leaves the rules stranded mid-page while the
   answers still wrap at a comfortable measure of their own. */
.wide{max-width:820px}
/* A container that is deliberately narrower than the page and centred
   in it, rather than held to the left gutter. Named so the layout
   check can tell an intentional centring from a broken one. */
.wrap--centred{max-width:1080px}
.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
.skip{position:absolute;left:-9999px;top:0;background:var(--color-deep-navy);color:var(--color-warm-parchment);padding:var(--spacing-12) var(--spacing-20);z-index:200;border-radius:0 0 var(--radius-small) 0;text-decoration:none}
.skip:focus{left:0;top:0}

/* Sections stack with no decorative dividers - only 1px rules in the
   navy tint mark a break. Padding never drops below 32px, at any
   width: the spacious rhythm is the brand signal. */
.band{padding:var(--section-gap) 0}
.band--tight{padding:var(--spacing-80) 0}
.band--lift{background:var(--surface-linen-lift)}
.band--dark{background:var(--surface-slate-teal);color:var(--ink-reverse)}
/* Two dark bands in a row are one dark block as far as the eye is
   concerned, and stacking a full section gap between them opens a
   band of empty colour with nothing in it. The join keeps a gap, not
   two. */
.band--dark + .band--dark{padding-top:var(--spacing-40)}
.band--rule{border-top:1px solid var(--rule)}
.band--flush{padding-top:0}
/* padding-top, not the shorthand. These sit on the same element as
   .wrap, and a shorthand with 0 in the horizontal slot silently wipes
   the page padding that .wrap sets - which put every page head hard
   against the left edge of the screen. The home page escaped it
   because .hero is on a wrapper of its own. */
.pagehead{padding-top:var(--spacing-80)}
.pagehead--lone{padding-top:var(--spacing-80)}
/* A page head followed by a full band stacked 80px onto 120px, which
   read as a hole. One gap, set here, whether the next band is flush or
   not. Element-qualified so it outranks .band--flush. */
.pagehead + section.band,.pagehead + div.band{padding-top:var(--spacing-48)}
/* Same again when the page head is wrapped in a wash: the head is no
   longer the band's sibling, the wash is, and without this the first
   section runs straight into the bottom of the head. */
.wash + section.band,.wash + div.band{padding-top:var(--spacing-80)}
/* A page head inside a wash carries its own closing space, so the
   gradient has somewhere to fade and the first group below is not
   crowded up against the lede. */
.wash > .pagehead{padding-top:var(--spacing-120);padding-bottom:var(--spacing-40)}
/* A headline wants more room than a paragraph measure allows. */
.pagehead .narrow{max-width:820px}
/* The last dark band runs straight into the dark footer, so its lower
   padding would double up into one very deep navy field. */
.band--dark:last-child{padding-bottom:var(--spacing-64)}

/* ---- reversed type inside dark sections ---- */
.band--dark h1,.band--dark h2,.band--dark h3,.band--dark h4{color:var(--ink-reverse)}
.band--dark a{color:var(--ink-reverse)}
.band--dark a:hover{color:var(--color-mist-blue)}
.band--dark .muted,.band--dark .lede{color:var(--ink-reverse-muted)}
.band--dark .label,.band--dark .eyebrow{color:var(--color-mist-blue)}
.band--dark :focus-visible{outline-color:var(--color-signal-mint)}

/* A parchment card lifted inside a dark surround - the signature
   Avenir composition. */
.lifted{background:var(--surface-warm-parchment);color:var(--ink);border-radius:var(--radius-large);padding:var(--spacing-80)}
.lifted h2,.lifted h3{color:var(--ink)}
.lifted .lede,.lifted p{color:var(--ink)}
.lifted .label,.lifted .eyebrow{color:var(--ink-muted)}
.lifted a{color:var(--ink)}
.lifted .muted{color:var(--ink-muted)}
.lifted :focus-visible{outline-color:var(--color-deep-navy)}
.lifted > :last-child{margin-bottom:0}

.hero{padding:var(--spacing-160) 0 var(--spacing-80)}
.hero__grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:var(--spacing-80);align-items:end}
.hero h1{max-width:18ch}
/* The hero column runs a touch wider than the rest of the site: the
   headline and the lede are the first thing read and 44ch broke them
   into more lines than the space needed. */
.hero .lede{max-width:52ch}
/* In the hero the three facts stack rather than run as a row. On one
   line they ran the full width of the column and pulled the eye off
   the headline; wrapped, they are a stack anyway, and a separator
   left hanging at the end of a line is the giveaway that it was an
   accident. Stacked on purpose, the dots have nothing to separate. */
.hero .credentials{flex-direction:column;gap:var(--spacing-8);max-width:52ch}
.hero .credentials li:not(:last-child)::after{content:none}
.pagehead h1,.tool__masthead h1{
  /* Two lines rather than one long one. A 40px headline running the
     full column reads as a strapline, not a title. */
  max-width:20ch;
  font-size:clamp(var(--text-heading),1.6rem + 1vw,var(--text-heading-lg));
  line-height:var(--leading-heading-lg);letter-spacing:var(--tracking-heading-lg)}

.grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--element-gap)}
.grid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--element-gap)}
.grid--wide{gap:var(--spacing-80);align-items:start}
.grid--centre{align-items:center}
.centred{text-align:center}
.centred .lede,.centred p{margin-left:auto;margin-right:auto}

/* ============================================================
   TYPE ROLES
   ============================================================ */

/* Fragment Mono, all caps, tracked - used only for category labels
   and functional metadata, never for prose. */
.label,.eyebrow{
  font-family:var(--font-fragment-mono-label);
  font-weight:var(--font-weight-medium);
  font-size:var(--text-caption);
  line-height:var(--leading-caption);
  letter-spacing:var(--tracking-caption);
  text-transform:uppercase;
  color:var(--ink-muted);
  margin:0 0 var(--spacing-24);
  display:block;
}
/* An eyebrow that is a link back up the hierarchy is a control, so it
   gets a control-sized target. */
.eyebrow a{color:inherit;text-decoration-thickness:1px;display:inline-flex;align-items:center;min-height:44px;margin:calc(var(--spacing-12) * -1) 0}
.lede{font-size:var(--text-lede);line-height:var(--leading-lede);color:var(--ink);max-width:44ch}
/* One measure for the whole text column, not one per element. Set on
   the paragraphs it was worse than doing nothing: the rule outranked
   .lede, so the lede took 66 characters at its own larger size and ran
   a hundred and fifty pixels past the body text under it. A width on
   the column is independent of font size, so the eyebrow, the heading,
   the lede, the body and the button all end on the same line. */
/* The portrait column is exempt: this cap exists to hold text to a
   reading measure, and applying it to a column holding a picture left
   the picture centred inside a 608px box that was itself sitting at
   the left of a much wider track. */
.grid--wide > div:not(.card):not(.portrait-col){max-width:38rem}
.small{font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm)}
.muted{color:var(--ink-muted)}

/* ============================================================
   HEADER - a centred floating bar that gains a parchment ground
   and a soft shadow on scroll.
   ============================================================ */
.site-header{position:sticky;top:0;z-index:100;background:transparent;
  transition:background var(--duration-ui-transition) var(--easing-ui-transition),
             box-shadow var(--duration-ui-transition) var(--easing-ui-transition),
             transform var(--duration-ui-transition) var(--easing-ui-transition)}
/* Out of the way going down, back on the way up. It returns whenever
   focus enters it, so a keyboard user is never tabbing into something
   parked off screen. */
.site-header.is-tucked{transform:translateY(-100%)}
.site-header.is-scrolled{background:var(--surface-warm-parchment);box-shadow:var(--shadow-nav-resting)}

/* Tool pages invert, so the bar over them inverts with them. A
   parchment strip above a navy page reads as a seam. */
.page--tool .site-header.is-scrolled{background:var(--surface-slate-teal);box-shadow:0 4px 10px 0 rgba(0,0,0,.28)}
.page--tool .logo{color:var(--ink-reverse)}
.page--tool .logo:hover{color:var(--color-mist-blue)}
.page--tool .mainnav a{color:var(--ink-reverse)}
.page--tool @media (hover:hover){.mainnav a:hover{background:var(--color-mist-blue);color:var(--color-deep-navy)}}
.page--tool .mainnav a[aria-current="page"]{background:rgba(247,246,245,.12)}
.page--tool .navtoggle{color:var(--ink-reverse);border-color:var(--color-warm-parchment)}
@media (hover:hover){.page--tool .navtoggle:hover{background:var(--color-mist-blue);color:var(--color-deep-navy)}}
.page--tool .navtoggle[aria-expanded="true"]{background:rgba(247,246,245,.16);color:var(--ink-reverse);border-color:var(--color-warm-parchment)}
.page--tool .site-header :focus-visible{outline-color:var(--color-signal-mint)}
/* The header action goes quiet here: the masthead already carries the
   mint button, and the accent appears once per screen. */
.page--tool .mainnav .btn--secondary{background:transparent;color:var(--ink-reverse);border-color:var(--color-warm-parchment)}
.page--tool .mainnav .btn--secondary:hover{background:var(--color-mist-blue);color:var(--color-deep-navy);border-color:var(--color-mist-blue)}
html:not(.js) body.page--tool .site-header{background:var(--surface-slate-teal);border-bottom-color:var(--rule-reverse)}
/* Without JavaScript there is no scroll listener, so the bar simply
   carries its ground from the start rather than floating over the
   text it would otherwise overlap. */
html:not(.js) .site-header{background:var(--surface-warm-parchment);border-bottom:1px solid var(--rule)}
.site-header__inner{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-24);min-height:var(--header-h)}
/* The wordmark is inline SVG filled with currentColor, so it takes
   the ink of whatever it sits on. Set the colour here, not in the
   artwork. */
/* min-height, not a taller drawing: the wordmark is 5.7:1 and sits
   about 30px tall at this width, so the link needs its own hit area to
   meet the 44px target rule. */
.logo{display:inline-flex;align-items:center;min-height:44px;padding:var(--spacing-4) 0;text-decoration:none;flex-shrink:0;color:var(--ink);
  transition:color var(--duration-ui-transition) var(--easing-ui-transition)}
.logo:hover{color:var(--ink-muted)}
/* Sized so the lettering matches its old optical size: the new
   lockup is 6.7:1 where the previous one was 5.7:1, and about a
   fifth of its width is the symbol. */
.logo__mark{display:block;width:178px;height:auto}
.mainnav{margin-left:auto}
.mainnav__list{display:flex;align-items:center;gap:var(--spacing-4);list-style:none;margin:0;padding:0;flex-wrap:wrap}
.mainnav a{font-size:var(--text-body-sm);letter-spacing:var(--tracking-body-sm);color:var(--ink);text-decoration:none;padding:var(--spacing-8) var(--spacing-12);border-radius:var(--radius-small);display:inline-flex;align-items:center;min-height:44px;transition:background var(--duration-ui-transition) var(--easing-ui-transition),color var(--duration-ui-transition) var(--easing-ui-transition)}
.mainnav a:hover{background:var(--color-mist-blue);color:var(--color-deep-navy)}
.mainnav a[aria-current="page"]{background:var(--surface-navy-veil);font-weight:var(--font-weight-medium)}
.mainnav__action{margin-left:var(--spacing-8)}
/* The nav link rule is more specific than the button variant, so the
   button has to restate its own colours or it renders navy on navy. */
.mainnav .btn--secondary{color:var(--color-warm-parchment);background:var(--color-deep-navy)}
.mainnav .btn--secondary:hover{color:var(--color-deep-navy);background:var(--color-mist-blue)}
.mainnav .btn--primary{color:var(--color-deep-navy);background:var(--color-signal-mint)}
.navtoggle{display:none;align-items:center;justify-content:center;background:transparent;
  border:1px solid var(--color-deep-navy);border-radius:var(--radius-buttons);
  width:44px;height:44px;padding:0;cursor:pointer;color:var(--ink);
  transition:background var(--duration-ui-transition) var(--easing-ui-transition)}
/* Guarded: on a touch screen :hover latches after a tap, so the button
   kept the hover fill while the menu was open and read as disabled.
   The open state is styled deliberately instead. */
@media (hover:hover){.navtoggle:hover{background:var(--color-mist-blue)}}
.navtoggle[aria-expanded="true"]{background:var(--surface-navy-veil);border-color:var(--color-deep-navy)}
.navtoggle__bars{width:16px;height:1px;background:currentColor;box-shadow:0 -5px 0 currentColor,0 5px 0 currentColor;display:block}

/* ============================================================
   BUTTONS
   8px radius throughout - a pill would read as consumer SaaS
   rather than consultancy.
   ============================================================ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--spacing-8);
  font-family:var(--font-inter-tight-system);font-weight:var(--font-weight-medium);
  font-size:var(--text-body);line-height:1.2;letter-spacing:0;
  padding:var(--spacing-16) var(--spacing-32);
  border-radius:var(--radius-buttons);border:1px solid transparent;
  min-height:52px;text-decoration:none;cursor:pointer;
  transition:background var(--duration-ui-transition) var(--easing-ui-transition),
             border-color var(--duration-ui-transition) var(--easing-ui-transition),
             box-shadow var(--duration-ui-transition) var(--easing-ui-transition),
             transform var(--duration-ui-transition) var(--easing-ui-transition),
             color var(--duration-ui-transition) var(--easing-ui-transition);
}
/* Every call to action carries the same arrow, so the shape of the
   thing tells you it goes somewhere before you have read the label. It
   slides on hover: the movement is the affordance. The navigation bar
   is the exception - at that size an arrow is a smudge. */
.btn--primary::after,.btn--ghost::after,.btn--secondary::after{
  content:"";flex:none;width:1.05em;height:1.05em;margin-left:var(--spacing-4);
  background:currentColor;
  -webkit-mask:var(--icon-arrow) center/contain no-repeat;
          mask:var(--icon-arrow) center/contain no-repeat;
  transition:transform var(--duration-ui-transition) var(--easing-ui-transition)}
@media (hover:hover){
  .btn:hover::after{transform:translateX(4px)}
}
.mainnav .btn::after{content:none}
/* The one warm element on any screen, and the only place Solar
   appears. It always says the same thing. */
/* Flat mint, no cast beneath it: the colour is loud enough on its own,
   and a shadow under a bright green button reads as a sticker. The
   label stays navy - mint is bright enough that white on it is 2:1 and
   unreadable, navy is 7.08:1. */
.btn--primary{background:var(--color-signal-mint);color:var(--color-deep-navy);border-color:var(--color-signal-mint)}
.btn--primary:hover{background:var(--color-signal-mint-dark);border-color:var(--color-signal-mint-dark);color:var(--color-deep-navy)}
@media (hover:hover){.btn--primary:hover{transform:translateY(-1px)}}
.btn--primary:active{transform:translateY(0)}
.btn--secondary{background:var(--color-deep-navy);color:var(--color-warm-parchment);border-color:var(--color-deep-navy)}
.btn--secondary:hover{background:var(--color-mist-blue);color:var(--color-deep-navy);border-color:var(--color-mist-blue)}
.btn--ghost{background:transparent;color:var(--ink);border-color:var(--color-deep-navy)}
.btn--ghost:hover{background:var(--color-mist-blue);color:var(--color-deep-navy)}
.btn--sm{padding:var(--spacing-8) var(--spacing-16);font-size:var(--text-body-sm);letter-spacing:var(--tracking-body-sm);min-height:44px}
/* Same specificity trap as the nav: the reversed link colour outranks
   the button variant, so each variant restates its ink here. */
.band--dark .btn--primary{color:var(--color-deep-navy)}
.band--dark .btn--primary:hover{color:var(--color-deep-navy)}
.band--dark .btn--secondary{color:var(--color-warm-parchment)}
.band--dark .btn--secondary:hover{color:var(--color-deep-navy)}
.lifted .btn--primary{color:var(--color-deep-navy)}
.lifted .btn--ghost{color:var(--ink);border-color:var(--color-deep-navy)}
.band--dark .btn--ghost{color:var(--ink-reverse);border-color:var(--color-warm-parchment)}
.band--dark .btn--ghost:hover{background:var(--color-mist-blue);color:var(--color-deep-navy);border-color:var(--color-mist-blue)}
.btn-row{display:flex;gap:var(--element-gap);flex-wrap:wrap;align-items:center;margin:0}
.btn-row--centre{justify-content:center}

/* tertiary: an underline-style link, never a third button */
.textlink{
  font-family:var(--font-inter-tight-system);font-weight:var(--font-weight-medium);
  font-size:var(--text-body-sm);letter-spacing:var(--tracking-body-sm);
  display:inline-flex;align-items:center;gap:var(--spacing-8);min-height:44px;
  text-decoration:underline;text-underline-offset:4px;
}
.textlink::after{content:"\2192";transition:transform var(--duration-ui-transition) var(--easing-ui-transition)}
.textlink:hover::after{transform:translateX(4px)}

/* ============================================================
   TAGS AND STATUS
   ============================================================ */
.tag,.pill{
  display:inline-flex;align-items:center;gap:var(--spacing-8);
  font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);
  font-size:var(--text-caption);line-height:var(--leading-caption);
  letter-spacing:var(--tracking-caption);text-transform:uppercase;
  padding:var(--spacing-8) var(--spacing-12);border-radius:var(--radius-tags);
  border:1px solid var(--color-deep-navy);white-space:nowrap;
}
/* Availability reads from the word. The fill and the outline agree
   with it; neither is asked to carry it alone, and Avenir's success
   and warning colours stay where they belong, on states. */
/* Signal Mint, not navy. Availability is the one fact a visitor is
   scanning this page for, and the accent is the only colour on the
   site that finds the eye on its own. Navy on mint is 7.1:1, and the
   distinction from an outlined "coming soon" still survives greyscale
   and colour vision deficiency, which is what the rule was protecting.
   One value, every context: mint reads on parchment, on linen and on
   the dark band, so it needs no per-band correction the way the
   outlined variant does. */
.pill--now{background:var(--color-signal-mint);color:var(--color-deep-navy);border-color:var(--color-signal-mint)}
.pill--soon{background:transparent;color:var(--ink)}
.pill__dot{width:5px;height:5px;border-radius:var(--radius-circle);background:currentColor;flex:none}
.pill--soon .pill__dot{background:transparent;box-shadow:inset 0 0 0 1px currentColor}
.band--dark .pill--soon{color:var(--ink-reverse);border-color:var(--color-warm-parchment)}
/* Those two rules are for a pill sitting on the dark band itself -
   the masthead of a tool page. A pill inside a card is sitting on the
   card's light surface instead, so it needs the light-band colours
   back. Same correction the card's headings and links get above.
   Without it, a "coming soon" pill on a tool card in a dark section
   is reversed ink on linen, which axe reports and nobody can read. */
.band--dark .card .pill--soon,.band--dark .toolcard .pill--soon{color:var(--ink);border-color:var(--color-deep-navy)}

/* ============================================================
   CARDS
   ============================================================ */
/* A light surface always states its own ink. Inside a dark band the
   section colour would otherwise cascade into the card and leave
   parchment text on a white ground. */
.card,.toolcard,.enquiry,.lifted{color:var(--ink)}
.band--dark .card h2,.band--dark .card h3,.band--dark .card a,
.band--dark .toolcard h3,.band--dark .toolcard a{color:var(--ink)}
.band--dark .card a:hover,.band--dark .toolcard a:hover{color:var(--ink-muted)}
.band--dark .card :focus-visible,.band--dark .toolcard :focus-visible,
.band--dark .enquiry :focus-visible{outline-color:var(--color-deep-navy)}
.card,.toolcard{
  position:relative;
  background:var(--surface-linen-lift);
  border-radius:var(--radius-cards);
  padding:var(--card-padding);
  transition:transform var(--duration-card-hover) var(--easing-card-hover);
}
.card > :last-child,.toolcard > :last-child{margin-bottom:0}
/* An icon above a card heading. Mint, drawn in strokes rather than
   filled: a filled shape at this size reads as a button. It carries
   no meaning the heading does not already carry, so it is hidden from
   assistive technology and never asked to stand in for a word. */
.card__icon{display:block;width:28px;height:28px;margin-bottom:var(--spacing-20);
  color:var(--color-signal-mint);flex:none}
.band--dark .card .card__icon{color:var(--color-signal-mint)}
/* A card that ends in a link ends in a 44px target wrapped around about
   19px of text, so a third of that box is empty space sitting on top of
   the card's own padding. Pulled back by the difference, so the gap you
   see under the words is the padding and nothing else. Cards ending in
   a paragraph of text are left alone. */
.card > p:last-child:has(> .textlink){margin-bottom:-12px}
.band--lift .card,.band--lift .toolcard{background:var(--surface-warm-parchment)}
.toolcard{display:flex;flex-direction:column;align-items:flex-start}
.toolcard > *{max-width:100%}
.toolcard__list,.toolcard__sum,.toolcard__title{width:100%}
.toolcard:hover{transform:translateY(-4px)}
.card h3,.toolcard__title{margin-bottom:var(--spacing-12)}
.toolcard__title{font-family:var(--font-lora-editorial);font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm);margin-top:var(--spacing-24)}
.toolcard__title a{text-decoration:none;display:inline-block;padding-block:var(--spacing-8);margin-block:calc(var(--spacing-8) * -1)}
/* Stretched over the whole card, so anywhere on it is the link.
   z-index matters: the overlay paints at the title's place in the
   stacking order, so without it the summary, the feature list and the
   footer below sit on top and swallow the click. */
.toolcard__link::after{content:"";position:absolute;inset:0;border-radius:inherit;z-index:1}
.toolcard:hover .toolcard__title{text-decoration:underline;text-underline-offset:4px}
/* The focus ring belongs on the card, not on the few words of the
   title, so it is drawn on the stretched overlay instead. */
.toolcard__link:focus-visible{outline:none}
.toolcard__link:focus-visible::after{outline:3px solid var(--color-deep-navy);outline-offset:4px;border-radius:var(--radius-cards)}
.band--dark .toolcard__link:focus-visible::after,
.tool .toolcard__link:focus-visible::after{outline-color:var(--color-signal-mint)}
.toolcard .textlink{cursor:pointer}
.toolcard__sum{font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm);margin-bottom:var(--spacing-20)}
.toolcard__list{list-style:none;padding:0;margin:0 0 var(--spacing-24);font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm);color:var(--ink-muted)}
.toolcard__list li{position:relative;padding-left:var(--spacing-20);margin-bottom:var(--spacing-8)}
.toolcard__list li::before{content:"";position:absolute;left:0;top:.6em;width:6px;height:1px;background:var(--color-deep-navy)}
.toolcard__cta{margin:auto 0 0}

/* Three facts under the headline, set the way a byline is: the body
   face, sentence case, quiet. Boxed as chips they read as controls;
   as a mono list with coloured dots they read as a feature grid on a
   software landing page. Neither is what they are. */
.credentials{list-style:none;padding:0;margin:var(--spacing-32) 0 0;max-width:44ch;
  display:flex;flex-wrap:wrap;gap:var(--spacing-8) var(--spacing-16);
  font-size:var(--text-body-sm);line-height:var(--leading-body-sm);
  letter-spacing:var(--tracking-body-sm);color:var(--ink-muted)}
/* The separator sits after each item rather than before the next one.
   When the row wraps, a dot trailing the end of a line reads as "this
   continues"; the same dot at the head of the new line reads as a
   bullet that lost its list. */
.credentials li:not(:last-child)::after{content:"\00B7";margin-left:var(--spacing-16);
  font-size:1.5em;line-height:0;vertical-align:-.05em;
  color:var(--ink-muted);opacity:.6}

/* A translucent ring rather than a hard edge, so the wash behind shows
   through it and the card sits in the hero instead of on top of it.
   background-clip keeps the white fill inside the border, which is what
   lets the border stay see-through at all. */
.hero__aside{position:relative;background:var(--surface-linen-lift);background-clip:padding-box;
  border:15px solid rgba(255,255,255,.45);
  border-radius:calc(var(--radius-cards) + 15px);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  padding:var(--spacing-40)}
.hero__aside h2{font-family:var(--font-lora-editorial);font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm)}
.hero__aside > :last-child{margin-bottom:0}
/* The portrait is positioned out of the panel's top corner and takes
   no space in it. Reserving a column of padding for something that
   overhangs the edge left a gutter down the whole right side with
   nothing in it, and pulled every line of the paragraph short. */

/* ---- maxims ----
   The two lines Mike wants under the tools intro. Unattributed on
   purpose: both are widely quoted and neither has a source anyone
   can stand behind, so a name under them would be an invented
   citation on a site that refuses to invent anything else. Lora at
   regular weight, hung off a Mist Blue rule - quiet enough to read
   as an aside rather than as a claim the page is making. */
/* One epigraph, not two columns. Side by side they were two unequal
   blocks of Lora sitting directly under a much larger Lora headline,
   at a third measure again - three different line lengths stacked in
   one page head, which is what made it read as a pile rather than an
   introduction. Stacked on a single rule at the lede's own measure,
   they are one quiet aside and the head has one left edge and one
   width the whole way down. */
.maxims{margin:0;padding-left:var(--spacing-24);
  border-left:2px solid var(--color-mist-blue);max-width:44ch}
.hero__aside .maxims{padding-left:0;border-left:0;max-width:none}
.maxims p{margin:0;font-family:var(--font-lora-editorial);font-weight:var(--font-weight-regular);
  font-size:var(--text-body-lg);line-height:1.3;letter-spacing:var(--tracking-heading-sm);
  color:var(--ink-muted)}
.maxims p + p{margin-top:var(--spacing-24);padding-top:var(--spacing-24);border-top:1px solid var(--rule)}
@media (max-width:767px){.maxims{margin-top:var(--spacing-32)}.maxims p{font-size:var(--text-lede);line-height:1.35}}

/* Every paragraph in a page head sits at the lede's measure, not
   .narrow's 68ch. Set here once rather than as a class each paragraph
   has to remember, because the failure is silent: one plain paragraph
   under a lede steps out past everything around it and the head reads
   as a pile of different line lengths. Body ink, deliberately - a
   supporting sentence is still content, and grey makes real copy look
   like an annotation. */
.pagehead .narrow > p:not(.btn-row){max-width:44ch}

/* ---- lifecycle grouping on the tools index ---- */
.stage{margin-bottom:var(--spacing-80)}
.stage:last-child{margin-bottom:0}
.stage__head{display:flex;align-items:baseline;gap:var(--spacing-24);padding-bottom:var(--spacing-8);margin-bottom:var(--spacing-40);border-bottom:1px solid var(--rule);flex-wrap:wrap}
.stage__head h2{font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm);margin:0}
/* A fixed column for the label, so every stage heading starts on the
   same vertical line. Left to size itself, "Across the year" is 49px
   wider than "Stage one" and pushed its heading that far right of the
   two above it. */
.stage__n{flex:none;min-width:9rem;
  font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:var(--leading-caption);letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink-muted)}

/* ---- steps ----
   Four cards, so each step is a discrete thing you can read on its own
   rather than a column of text floating under a rule. On the dark band
   they are tinted panels rather than white cards: four solid white
   blocks would outweigh the tool cards above them and undo the reason
   this section is dark in the first place. The number sits in its own
   row above the heading, in mint, so the sequence reads at a glance. */
.steps{counter-reset:s;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:var(--element-gap);list-style:none;padding:0;margin:var(--spacing-64) 0 0}
.steps li{counter-increment:s;display:flex;flex-direction:column;
  padding:var(--spacing-32) var(--spacing-24) var(--spacing-32);
  background:var(--surface-navy-veil);border-radius:var(--radius-cards)}
/* The numeral is set in the editorial serif at heading size: at label
   size in mono it read as metadata about the step rather than as the
   step's own number, which is the one thing the sequence needs. */
.steps li::before{content:counter(s,decimal-leading-zero);display:block;
  font-family:var(--font-lora-editorial);font-size:var(--text-heading-lg);
  line-height:1;letter-spacing:var(--tracking-heading-lg);
  color:var(--color-signal-mint);margin-bottom:var(--spacing-24)}
.steps h3{font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);
  letter-spacing:var(--tracking-heading-sm);margin-bottom:var(--spacing-12)}
.steps p{font-size:var(--text-body-sm);line-height:var(--leading-body-sm);
  letter-spacing:var(--tracking-body-sm);margin:0;color:var(--ink-muted)}
.band--dark .steps li{background:rgba(247,246,245,.05)}
.band--dark .steps p{color:var(--ink-reverse-muted)}
.band--lift .steps li{background:var(--surface-warm-parchment)}

/* ---- FAQ ---- */
.faq{border-bottom:1px solid var(--rule)}
.faq summary{cursor:pointer;list-style:none;padding:var(--spacing-24) var(--spacing-48) var(--spacing-24) 0;position:relative;
  font-family:var(--font-lora-editorial);font-weight:var(--font-weight-regular);
  font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm);
  color:var(--ink);min-height:48px;display:flex;align-items:center}
.faq summary::-webkit-details-marker{display:none}
/* Drawn as two bars rather than set as "+" and "−". Swapping one
   character for another cannot be animated; collapsing the upright of a
   cross into the bar beneath it can, and it says opening and closing
   rather than plus and minus. */
.faq summary::before,.faq summary::after{content:"";position:absolute;right:var(--spacing-8);top:50%;
  background:var(--ink-muted);border-radius:1px}
.faq summary::after{width:14px;height:1.5px;transform:translateY(-50%)}
.faq summary::before{width:1.5px;height:14px;transform:translate(-6.25px,-50%);
  transition:transform var(--duration-reveal) var(--easing-reveal),
             opacity var(--duration-reveal) var(--easing-reveal)}
.faq[open] summary::before{transform:translate(-6.25px,-50%) scaleY(0);opacity:0}
.faq summary:hover{color:var(--ink-muted)}
.faq__a{padding:0 var(--spacing-48) var(--spacing-32) 0;max-width:var(--measure)}
/* The answer slides rather than appearing. Browsers cannot animate to
   `height:auto` on their own, and a <details> element has no height to
   animate at all until it opens - so this needs two newer features:
   interpolate-size, which lets a transition reach a keyword, and
   ::details-content, which is the box the browser creates around the
   answer. Both are behind @supports, so a browser without them opens
   the answer instantly, exactly as before. No script either way. */
@supports (interpolate-size: allow-keywords) and (selector(::details-content)) {
  :root{interpolate-size:allow-keywords}
  .faq::details-content{
    block-size:0;overflow:hidden;opacity:0;
    transition:block-size var(--duration-reveal) var(--easing-reveal),
               content-visibility var(--duration-reveal) var(--easing-reveal) allow-discrete,
               opacity var(--duration-reveal) var(--easing-reveal);
  }
  .faq[open]::details-content{block-size:auto;opacity:1}
}

.faq__a > :last-child{margin-bottom:0}

/* ---- callout ---- */
.callout{background:var(--surface-dusk-stone);border-radius:var(--radius-cards);padding:var(--spacing-40)}
/* A notice: the same shape as a callout, in the accent rather than in
   stone, so a statement the page is making on its own behalf does not
   look like one more content card. The mint is at 8% behind navy
   text, which leaves the body copy at its normal contrast. */
.notice{background:rgba(24,207,151,.08);border:1px solid rgba(24,207,151,.5);
  border-radius:var(--radius-cards);padding:var(--spacing-40)}
.notice > :last-child{margin-bottom:0}
/* A card in a two-column grid is twice the width of one in a
   three-column grid, and the 24px that suits the narrow one leaves the
   wide one looking unlined - particularly beside a callout, which the
   two-column cards on this site always are. Same padding, so a pair
   reads as a pair rather than as two different kinds of panel. */
.grid2 > .card{padding:var(--spacing-40)}
.callout__title{font-family:var(--font-lora-editorial);font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm);margin-bottom:var(--spacing-12)}
.callout > :last-child{margin-bottom:0}
/* Frosted, not just tinted. The contour map runs behind this panel,
   and a flat 8% wash let every line through at full definition, so the
   heading sat on top of terrain rather than on a surface. Blurring the
   backdrop puts a pane of glass between the two. */
.band--dark .callout,.tool .callout{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
.band--dark .callout{background:rgba(247,246,245,.08)}
.band--dark .callout__title{color:var(--ink-reverse)}

/* ---- prose ---- */
.prose{max-width:var(--measure)}
.prose h2{margin-top:var(--spacing-64)}
.prose h2:first-child{margin-top:0}
.prose h3{margin-top:var(--spacing-48)}
.prose ul,.prose ol{padding-left:var(--spacing-20);margin:0 0 var(--spacing-20)}
.prose li{margin-bottom:var(--spacing-8)}
.scroll-x{overflow-x:auto;margin:0 0 var(--spacing-32);border-radius:var(--radius-small)}
.scroll-x:focus-visible{outline:3px solid var(--color-deep-navy);outline-offset:2px}
.scroll-x table{margin-bottom:0}
.prose table{border-collapse:collapse;width:100%;margin:0 0 var(--spacing-32);font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm)}
.prose th,.prose td{text-align:left;padding:var(--spacing-16);border-bottom:1px solid var(--rule);vertical-align:top}
.prose th{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink-muted)}
.prose__meta{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:1.6;letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink-muted);border-top:1px solid var(--rule);padding-top:var(--spacing-24);margin-top:var(--spacing-64)}
.pullquote{font-family:var(--font-lora-editorial);font-style:italic;font-size:var(--text-body-lg);line-height:var(--leading-body-lg);border-left:1px solid var(--rule);padding-left:var(--spacing-32);margin:var(--spacing-40) 0}

/* ============================================================
   FORM - inputs sit on Dusk Stone to read as recessed
   ============================================================ */
.enquiry{background:var(--surface-linen-lift);border-radius:var(--radius-cards);padding:var(--spacing-48)}
.band--lift .enquiry{background:var(--surface-warm-parchment)}
.field{margin-bottom:var(--spacing-24)}
.field label{display:block;font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:var(--leading-caption);letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink-muted);margin-bottom:var(--spacing-12)}
.field .hint{text-transform:none;letter-spacing:var(--tracking-body-sm)}
.field input,.field select,.field textarea{
  width:100%;font-family:var(--font-inter-tight-system);font-size:var(--text-body);line-height:var(--leading-body);
  padding:var(--spacing-12) var(--spacing-16);min-height:52px;
  /* Linen with a drawn border, not a filled grey block. Dusk Stone is
     the right weight for a divider and too heavy for six stacked
     fields - the form read as disabled. The border carries the shape
     instead, which is also what tells you the field is editable. */
  background:var(--surface-linen-lift);color:var(--ink);
  border:1px solid var(--rule-input);border-radius:var(--radius-inputs);
  transition:border-color var(--duration-ui-transition) var(--easing-ui-transition),background var(--duration-ui-transition) var(--easing-ui-transition)}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--color-deep-navy)}
.field input:focus,.field select:focus,.field textarea:focus{outline:3px solid var(--color-deep-navy);outline-offset:2px;background:var(--surface-linen-lift);border-color:var(--color-deep-navy)}
.field textarea{min-height:140px;resize:vertical}
.field--error input,.field--error select,.field--error textarea{border-color:var(--color-blush-error);border-width:2px}
.field__error{display:block;color:var(--error-ink);font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm);font-weight:var(--font-weight-medium);margin-top:var(--spacing-8)}
.field__error:empty{display:none}
.field--choices{border:0;padding:0;min-width:0}
.field--choices legend{display:block;padding:0;font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:var(--leading-caption);letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink-muted);margin-bottom:var(--spacing-8)}
.field--choices legend .hint{text-transform:none;letter-spacing:var(--tracking-body-sm)}
.field .choice{display:flex;align-items:center;gap:var(--spacing-12);min-height:44px;margin-bottom:var(--spacing-4)}
.field .choice input{flex:none;width:24px;height:24px;min-height:0;padding:0;margin:0;accent-color:var(--color-deep-navy);cursor:pointer}
.field .choice label{display:block;margin:0;padding:var(--spacing-8) 0;flex:1;cursor:pointer;font-family:var(--font-inter-tight-system);font-size:var(--text-body);line-height:var(--leading-body);letter-spacing:normal;text-transform:none;font-weight:var(--font-weight-regular,400);color:var(--ink)}
.field .choice input:focus-visible{outline:3px solid var(--color-deep-navy);outline-offset:2px}
.enquiry__submit{width:100%;margin-top:var(--spacing-8)}
.enquiry__note{margin:var(--spacing-16) 0 0;color:var(--ink-muted)}
.formstatus{margin:0 0 var(--spacing-24);padding:var(--spacing-16) var(--spacing-20);border-radius:var(--radius-small);background:var(--surface-dusk-stone);border-left:3px solid var(--color-fern-success);font-size:var(--text-body-sm);line-height:var(--leading-body-sm)}
.formstatus:empty{display:none}

/* ---- contact list ---- */
.contactlist{list-style:none;padding:0;margin:0}
.contactlist li{padding:var(--spacing-16) 0;border-bottom:1px solid var(--rule)}
.contactlist li:last-child{border-bottom:0;padding-bottom:0}
.contactlist a{display:inline-flex;align-items:center;min-height:44px}
.contactlist .k{display:block;font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:var(--leading-caption);letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink-muted);margin-bottom:var(--spacing-8)}

/* ---- figures ----
   Geometric schematics, not photographs and not fake screenshots. */
.figure{background:var(--surface-navy-veil);border-radius:var(--radius-cards);padding:var(--spacing-32);margin:0 0 var(--element-gap)}
.band--lift .figure{background:var(--surface-warm-parchment)}
/* ---- schematic ----
   Four steps, built from the site's own tokens rather than exported
   as a picture: sharp at any zoom, reflows, and its words are real
   text that can be searched, selected and read aloud.

   Laid out against its OWN width, not the viewport's. On a tool page
   this sits in a 672px column on the widest desktop, so a viewport
   media query would give it four columns in a space that fits two.
   A container query asks the right question. One column is the
   default, so a browser without container queries gets the stacked
   version rather than a crushed one.

   Everything inside a step's body is an abstraction of the tool's
   screen and says nothing on its own - the real screenshots are
   directly below - so those parts are hidden from assistive
   technology and only the numbers, titles and notes are read. */
.figure--schema{padding:var(--spacing-32);background:rgba(247,246,245,.04);border:0;
  container-type:inline-size}
.schema{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:minmax(0,1fr);gap:var(--spacing-40);align-items:start;
  /* A shaft with a head, not a bare chevron. A chevron on its own is
     the glyph a select control uses, so between two stacked steps it
     read as a control to open rather than as the flow carrying on.
     These are masks rather than background images so the colour stays
     on the mint token instead of being frozen into a data URI. */
  --schema-arrow-down:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 28' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 1.6V26.4M1.7 21.9 6 26.4l4.3-4.5'/%3E%3C/svg%3E");
  --schema-arrow-right:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 12' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1.6 6h24.8M21.9 1.7 26.4 6l-4.5 4.3'/%3E%3C/svg%3E")}
.schema__step{display:flex;flex-direction:column;gap:var(--spacing-16);position:relative;min-width:0}
/* The arrow sits in the gap so it never shifts the content, and is
   centred in it: the offset is half the gap plus half the arrow.
   Stacked, it points down under each step; in a row it points right. */
.schema__step:not(:last-child)::after{content:"";position:absolute;
  background-color:var(--color-signal-mint);opacity:.85;
  -webkit-mask:var(--schema-arrow-down) center/contain no-repeat;
  mask:var(--schema-arrow-down) center/contain no-repeat;
  width:12px;height:26px;
  top:auto;bottom:calc(var(--spacing-40) / -2 - 13px);left:50%;margin-left:-6px}
.schema__n{margin:0;font-family:var(--font-fragment-mono-label);font-size:var(--text-caption);
  letter-spacing:var(--tracking-caption);color:var(--color-signal-mint)}
.schema__title{margin:0;font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);
  letter-spacing:var(--tracking-heading-sm);color:var(--ink-reverse)}
.schema__body{margin:0;min-width:0}
.schema__note-text{margin:auto 0 0;font-size:var(--text-body-sm);line-height:var(--leading-body-sm);
  letter-spacing:var(--tracking-body-sm);color:var(--ink-reverse-muted)}

/* step one: what goes in */
.schema__cards{list-style:none;margin:0;padding:0;display:grid;gap:var(--spacing-8)}
.schema__card-in{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-12);
  background:var(--surface-warm-parchment);color:var(--ink);border-radius:var(--radius-cards);
  padding:var(--spacing-12) var(--spacing-16);
  font-size:var(--text-body-sm);line-height:var(--leading-body-sm);font-weight:var(--font-weight-medium)}
.schema__card-in b{font-variant-numeric:tabular-nums;flex:none}

/* step two: the one thing it does */
.schema__act{display:grid;justify-items:center;gap:var(--spacing-12);text-align:center;
  border:1px solid rgba(247,246,245,.16);border-radius:var(--radius-cards);
  padding:var(--spacing-32) var(--spacing-16);
  font-size:var(--text-body-sm);line-height:var(--leading-body-sm);color:var(--ink-reverse-muted)}
.schema__act .card__icon{width:32px;height:32px;margin:0}

/* step three: the screen that comes out */
.schema__screen{background:var(--surface-warm-parchment);border-radius:var(--radius-cards);
  overflow:hidden;min-width:0}
.schema__chrome{display:block;height:20px;background:var(--color-deep-navy)}
.schema__chrome--accent{background:var(--color-signal-mint)}
.schema__pad{padding:var(--spacing-12);display:grid;gap:var(--spacing-8);min-width:0}
.schema__split{display:flex;align-items:center;gap:var(--spacing-12);min-width:0}
.schema__donut{width:48px;height:48px;flex:none;transform:rotate(-90deg)}
.schema__key{list-style:none;margin:0;padding:0;display:grid;gap:4px;flex:1;min-width:0;
  font-size:11px;line-height:1.3;color:var(--ink)}
.schema__key li{display:flex;align-items:center;gap:5px;white-space:nowrap;min-width:0}
.schema__key b{margin-left:auto;font-variant-numeric:tabular-nums}
.schema__key--plain li{height:6px;background:var(--color-dusk-stone);border-radius:3px;display:block}
.schema__dot{width:7px;height:7px;border-radius:var(--radius-circle);flex:none}
.schema__dot--g{background:var(--color-fern-success)}
.schema__dot--a{background:var(--color-amber-warning)}
.schema__dot--r{background:var(--color-blush-error)}
.schema__stack{display:grid;gap:5px}
/* Each bar is one gradient rather than three elements: the stops are
   the split, so a section reads as a proportion at a glance. */
.schema__bar{display:block;height:7px;border-radius:4px;
  background:linear-gradient(to right,
    var(--color-fern-success) 0 var(--g),
    var(--color-amber-warning) var(--g) var(--a),
    var(--color-blush-error) var(--a) 100%)}
.schema__bar--1{--g:76%;--a:92%}
.schema__bar--2{--g:62%;--a:84%}
.schema__bar--3{--g:70%;--a:100%}
.schema__bar--4{--g:44%;--a:72%}
.schema__tiles{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}
.schema__tiles li{background:var(--color-linen-lift);border-radius:var(--radius-small);
  padding:5px 2px;text-align:center;font-variant-numeric:tabular-nums;
  font-size:12px;font-weight:var(--font-weight-medium);color:var(--ink)}
.schema__rows{display:grid;gap:5px}
.schema__row{display:flex;align-items:center;gap:6px;min-width:0}
.schema__rowlab{width:34%;height:5px;border-radius:3px;background:var(--color-dusk-stone);flex:none}
.schema__rowbar{height:7px;border-radius:4px;background:var(--color-deep-navy)}
.schema__rowbar--1{width:60%}
.schema__rowbar--2{width:46%}
.schema__rowbar--3{width:32%}
.schema__rowbar--4{width:20%}
.schema__note{display:block;height:22px;border-radius:var(--radius-small);background:var(--color-linen-lift)}
.schema__banner{display:block;height:22px;border-radius:var(--radius-small);background:var(--color-deep-navy)}
.schema__line{display:block;height:5px;border-radius:3px;background:var(--color-dusk-stone);width:100%}
.schema__line--46{width:46%}
.schema__line--94{width:94%}
.schema__line--72{width:72%}
.schema__card{display:flex;align-items:center;gap:var(--spacing-12);min-width:0;
  background:var(--color-linen-lift);border-radius:var(--radius-small);padding:var(--spacing-8)}

/* step four: what you are left with */
.schema__chips{list-style:none;margin:0;padding:0;display:grid;gap:var(--spacing-8)}
.schema__chips li{position:relative;padding:var(--spacing-12) var(--spacing-16) var(--spacing-12) var(--spacing-32);
  border:1px solid rgba(247,246,245,.16);border-radius:var(--radius-cards);
  font-size:var(--text-body-sm);line-height:var(--leading-body-sm);
  font-weight:var(--font-weight-medium);color:var(--ink-reverse)}
.schema__chips li::before{content:"";position:absolute;left:var(--spacing-16);top:50%;margin-top:-3.5px;
  width:7px;height:7px;border-radius:var(--radius-circle);background:var(--color-signal-mint)}

@container (min-width:560px){
  .schema{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--spacing-32)}
  /* Two across: arrows chain the pair in each row and stop there. The
     step after an even one is back at the left of the next row, so any
     arrow drawn from the right-hand column points at the wrong step -
     down leads the eye to the one below it, not the one it follows.
     The numbering carries the jump between rows instead. */
  .schema__step:not(:last-child)::after{top:var(--spacing-56, 52px);bottom:auto;
    left:auto;right:calc(var(--spacing-32) / -2 - 12px);margin-left:0;
    width:24px;height:12px;
    -webkit-mask-image:var(--schema-arrow-right);mask-image:var(--schema-arrow-right)}
  .schema__step:nth-child(2n)::after{content:none}
}
@container (min-width:980px){
  .schema{grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--spacing-40)}
  /* One row, so every arrow points right and the even-step rule above
     is put back. */
  .schema__step:not(:last-child)::after{content:"";top:var(--spacing-56, 52px);bottom:auto;
    left:auto;right:calc(var(--spacing-40) / -2 - 14px);margin-left:0;
    width:28px;height:12px;
    -webkit-mask-image:var(--schema-arrow-right);mask-image:var(--schema-arrow-right)}
}
@media (max-width:767px){.figure--schema{padding:var(--spacing-20)}}
.figure figcaption{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:1.6;letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink-muted);margin-top:var(--spacing-20);text-align:center}

/* A supplied photograph, held to the same width the portrait slot was
   and centred in its column rather than pinned to the top left of it.
   The image carries its own dimensions in the markup, so the column
   keeps its shape before the file arrives. */
/* A small window onto the same photograph, so the claim about who
   built the tools has a face beside it rather than only a name. It
   hangs off the card's top left corner, wearing the same translucent
   ring as the hero card and the portrait. The negative margins are the
   card's own padding plus the overhang, so the frame sits 20px outside
   the corner while the image still takes its space in the flow and
   nothing lands on top of the heading. */
.mugshot{position:absolute;z-index:1;top:-18px;right:-18px;margin:0;
  width:80px;height:80px;object-fit:cover;
  /* The card radius plus the border, not the lifted-panel radius plus
     a thicker one. At 8px and 28px it read as a chat avatar; on the
     card scale it reads as a picture pinned to the corner of a panel. */
  border:4px solid rgba(255,255,255,.55);
  border-radius:calc(var(--radius-cards) + 4px);
  box-shadow:0 12px 28px -14px rgba(11,46,60,.4)}
/* Taken out of the flow entirely, so the heading starts at the top of
   the card rather than below the picture. The card keeps a wider right
   padding instead, which is the room the window needs. */
.card--portrait{padding-right:calc(var(--spacing-40) + 88px)}
@media (max-width:767px){
  .mugshot{width:64px;height:64px;border-width:3px;top:-12px;right:-12px}
  .card--portrait{padding-right:calc(var(--spacing-32) + 64px)}
}

/* Centred across the column, top-aligned with the biography beside it:
   the two blocks should start on the same line. */
/* The column fills the row so the picture can sit in the middle of
     it, while the text beside it still starts at the top. Centring the
     whole row instead pushes the copy halfway down the page. */
.portrait-col{display:grid;justify-items:center;align-items:center;align-self:stretch}
/* On a wide screen the picture rises into the page head, where the
   right-hand side is empty anyway. It reads as one composition with
   the title rather than as an illustration bolted to the first
   paragraph, and it costs nothing: the space it moves into had
   nothing in it. Stacked layouts leave it where it is. */
@media (min-width:768px){
  .portrait-col{align-self:start;margin-top:calc(-1 * var(--spacing-80))}
}
@media (min-width:992px){
  .portrait-col{margin-top:calc(-1 * var(--spacing-160))}
}
.portrait{position:relative;margin:0;max-width:26rem}
/* Two facts lifted off the biography and set over the edges of the
   photograph. Nothing new is claimed: both already appear under the
   headline on the home page. They overhang by 28px, which is inside
   the slack between the photograph and the column it sits in, so
   nothing collides with the text beside it. */
.portrait__facts{list-style:none;margin:0;padding:0}
.portrait__fact{position:absolute;z-index:1;
  background:var(--surface-linen-lift);color:var(--ink);
  font-size:var(--text-body-sm);line-height:var(--leading-body-sm);
  letter-spacing:var(--tracking-body-sm);
  padding:var(--spacing-12) var(--spacing-16);
  border-radius:var(--radius-cards);
  box-shadow:0 10px 26px -12px rgba(11,46,60,.34)}
/* Four of them, alternating sides down the photograph. Alternating
   rather than evenly spaced: two pills on the same edge at similar
   heights read as a list that has come adrift, where left, right,
   left, right reads as annotation. Each is anchored to the edge it
   overhangs, so they cannot collide however long the words get. */
.portrait__fact--top{top:var(--spacing-40);left:calc(var(--spacing-28, 28px) * -1)}
.portrait__fact--mid{top:19%;right:calc(var(--spacing-28, 28px) * -1)}
.portrait__fact--low{bottom:24%;left:calc(var(--spacing-28, 28px) * -1)}
.portrait__fact--foot{bottom:var(--spacing-40);right:calc(var(--spacing-28, 28px) * -1)}
/* The same translucent ring the hero card wears, so the wash behind
   shows through the edge of the photograph too. */
.portrait img{display:block;width:100%;height:auto;
  border:15px solid rgba(255,255,255,.45);
  border-radius:calc(var(--radius-large) + 15px)}
/* Below 992 the column is under 400px wide, and a pill is most of
   that: overhanging one there does not annotate the photograph, it
   covers the person in it. They become an ordinary row underneath
   instead, which is what they are on a phone too. */
@media (max-width:991px){
  .portrait__facts{display:flex;flex-wrap:wrap;gap:var(--spacing-8);margin-top:var(--spacing-16)}
  .portrait__fact{position:static;box-shadow:none;background:var(--surface-navy-veil)}
}
@media (max-width:767px){
  .portrait img{border-width:6px}
}

/* ---- image slots ----
   A picture the client has yet to supply. It reserves its space with
   aspect-ratio so the composition on the page is already true, and
   says what it is rather than filling itself with stock photography.
   The ratio is a class because the CSP allows no inline styles. */
.imageslot{display:flex;align-items:center;justify-content:center;
  border:1px dashed var(--ink-muted);border-radius:var(--radius-large);
  background:var(--surface-navy-veil)}
.imageslot--4x3{aspect-ratio:4 / 3}
.imageslot--3x2{aspect-ratio:3 / 2}
.imageslot--1x1{aspect-ratio:1 / 1}
/* A portrait is capped rather than filling its column: a face blown up
   to the width of a text column is a poster, not a photograph beside a
   biography. */
.imageslot--3x4{aspect-ratio:3 / 4;max-width:26rem}
.band--lift .imageslot{background:var(--surface-warm-parchment)}
.imageslot__mark{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);
  font-size:var(--text-caption);letter-spacing:var(--tracking-caption);
  text-transform:uppercase;color:var(--ink-muted)}

/* ---- gaps ----
   Where copy or an asset is genuinely missing. Deliberately quiet: a
   note to the client, not a banner to a visitor. None of these should
   survive to launch. */
.gap{border:1px dashed var(--ink-muted);border-radius:var(--radius-cards);background:var(--surface-navy-veil);padding:var(--spacing-32);margin:0 0 var(--element-gap);color:var(--ink-muted);font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm)}
/* A note butting straight into whatever sits above it read as part of
   the same block. */
.btn-row + .gap,p + .gap{margin-top:var(--spacing-40)}
.gap__head{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:var(--leading-caption);letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--ink);margin:0 0 var(--spacing-16)}
.gap__body{margin:0}
.placeholder{background:var(--color-solar-accent);color:var(--color-deep-navy);padding:2px var(--spacing-8);border-radius:var(--radius-small);font-weight:var(--font-weight-medium);white-space:nowrap}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer{background:var(--surface-slate-teal);color:var(--ink-reverse);padding:var(--spacing-120) 0 var(--spacing-48)}
/* The contour map, on the footer and on the dark call to action band
   above it. Both canvases are created by the script and given the
   .has-topo class on their host, so a page with the script blocked
   carries no empty canvas and looks exactly as it did before.

   Behind everything, and it holds no information. No cursor change
   either: these blocks are full of links, and a crosshair over a link
   is a lie about what clicking does. */
.has-topo{position:relative;isolation:isolate;overflow:hidden}
.has-topo > .topo{position:absolute;inset:0;z-index:-1;display:block;width:100%;height:100%}
@media print{.topo{display:none}}
.site-footer a{color:var(--ink-reverse);text-decoration:none}
.site-footer a:hover{color:var(--color-mist-blue);text-decoration:underline;text-underline-offset:4px}
.site-footer :focus-visible{outline-color:var(--color-signal-mint)}
.foot__cols{display:grid;grid-template-columns:1.6fr 1fr 1fr;gap:var(--spacing-64);padding-bottom:var(--spacing-64);border-bottom:1px solid var(--rule-reverse)}
/* The brand column leads with the mark rather than a mono label; the
   two nav columns keep their labels. The heading is still a heading -
   its text is there for anyone not seeing the artwork. */
/* Both classes sit on the same element and .foot__head follows this
   rule in the file, so at equal specificity it won and set the mark in
   mist blue. Qualified so the mark keeps its own ink. */
.foot__head.foot__brandmark{margin-bottom:var(--spacing-24);color:var(--ink-reverse)}
.foot__wordmark{display:block;width:220px;height:auto}
.foot__head{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);font-size:var(--text-caption);line-height:var(--leading-caption);letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--color-mist-blue);margin:0 0 var(--spacing-20)}
.foot__brand p{max-width:34ch;color:var(--ink-reverse);font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm)}
.foot__list{list-style:none;padding:0;margin:0;font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm)}
.foot__list li{margin:0}
/* Footer links are standalone navigation, not links inside a sentence,
   so they carry the same 44px target as every other control. */
/* 28px rather than the 44px used elsewhere. At 44 the rows sat further
   apart than the heading sits from its own list, so each column read
   as six unrelated links rather than one group. 28 is as tight as this
   can honestly go: SC 2.5.8 sets the floor at 24 pixels, and the
   accessibility statement says which controls are held to which figure
   rather than claiming one number for all of them. */
.foot__list a{display:inline-flex;align-items:center;min-height:28px}
.legal{padding-top:var(--spacing-40);font-size:var(--text-body-sm);line-height:var(--leading-body-sm);letter-spacing:var(--tracking-body-sm);color:var(--ink-reverse)}
.legal p{margin:0 0 var(--spacing-8)}
/* Quieter than the company disclosures above it. Those are a legal
   requirement and read at full strength; this one is a note about the
   site, and at 9:1 it is still well past the AA floor. */
.legal__note{color:var(--ink-reverse-muted)}
.legal .placeholder{background:var(--color-solar-accent);color:var(--color-deep-navy)}

/* ============================================================
   MOBILE ACTION BAR
   Shown only once the in-page action has scrolled away, so the same
   action is never on screen twice.
   ============================================================ */
.thumbbar{position:fixed;left:0;right:0;bottom:0;z-index:110;display:none;
  padding:var(--spacing-12) var(--spacing-16) calc(var(--spacing-12) + env(safe-area-inset-bottom));
  background:var(--surface-warm-parchment);box-shadow:0 -4px 10px 0 rgba(11,46,60,.08)}
.thumbbar .btn{width:100%}

/* ============================================================
   RESPONSIVE - lg 991, md 767, sm 479
   ============================================================ */
@media (max-width:991px){
  :root{--section-gap:var(--spacing-80);--page-padding:var(--spacing-40)}
  .hero{padding:var(--spacing-32) 0 0}
  .hero__grid{grid-template-columns:1fr;gap:var(--spacing-48);align-items:start}
    .grid3,.steps{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--spacing-32)}
  .grid--wide{gap:var(--spacing-48)}
  .foot__cols{grid-template-columns:1fr 1fr;gap:var(--spacing-40)}
  .lifted{padding:var(--spacing-48)}

  /* With JavaScript the navigation collapses behind a button. Without
     it, it simply stacks and stays reachable - never a dead control. */
  .js .navtoggle{display:inline-flex}
  .js .mainnav{display:none}
  .js .mainnav.is-open{display:block}
  /* Opened as an overlay under the bar, not as another row inside it.
     Growing the header pushed the whole page down the moment the menu
     appeared; this leaves the bar exactly where it was. */
  .js .mainnav{position:absolute;top:100%;left:0;right:0;
    background:var(--surface-warm-parchment);border-top:1px solid var(--rule);
    box-shadow:var(--shadow-nav-resting);padding:var(--spacing-8) var(--page-padding) var(--spacing-20)}
  /* The tool pages invert, and their nav links are already set in
     parchment. On the default light panel that is parchment on
     parchment - an open menu with nothing legible in it. The panel
     inverts with the page. */
  .js body.page--tool .mainnav{background:#123c4c;
    border-top-color:var(--rule-reverse);box-shadow:0 10px 24px 0 rgba(0,0,0,.38)}
  /* Without JavaScript there is no toggle, so the nav simply stacks
     under the logo and stays reachable. */
  html:not(.js) .mainnav{width:100%;order:3;margin-left:0}
  html:not(.js) .site-header__inner{flex-wrap:wrap}
  .mainnav__list{flex-direction:column;align-items:stretch;gap:var(--spacing-4);padding:var(--spacing-8) 0 0}
  .mainnav a{padding:var(--spacing-12);min-height:48px}
  .mainnav__action{margin:var(--spacing-12) 0 0}
  .mainnav__action a{justify-content:center}
}
@media (max-width:767px){
  :root{--section-gap:var(--spacing-64);--measure:none;--text-lede:18px;--page-padding:var(--spacing-32)}
  /* The gap between a page head and the content under it, on a phone.
     Left to the desktop values it ran from 20px on one page to 120 on
     another, and the generous ones cost a third of a screen before
     anything is read. Set here as one number rather than left to add
     up from a head padding and a band padding that were chosen for a
     1440px window. */
  .hero{padding:var(--spacing-48) 0 var(--spacing-16)}
  .pagehead,.pagehead--lone{padding-top:var(--spacing-48)}
  .wash > .pagehead{padding-top:var(--spacing-48);padding-bottom:var(--spacing-16)}
  .wash + section.band,.wash + div.band{padding-top:var(--spacing-32)}
  .pagehead + section.band,.pagehead + div.band{padding-top:var(--spacing-32)}
  .logo__mark{width:182px}
  .foot__wordmark{width:196px}
  h1{letter-spacing:-.03em}
  .hero{padding:var(--spacing-64) 0 var(--spacing-48)}
  .band--tight{padding:var(--spacing-48) 0}
  .pagehead,.pagehead--lone{padding-top:var(--spacing-48)}
  /* minmax(0,1fr), not 1fr: a bare 1fr track cannot shrink below its
     content's min-content width, so one wide child stretches the grid
     past the viewport. */
  .grid2,.grid3,.steps,.grid--wide{grid-template-columns:minmax(0,1fr);gap:var(--spacing-32)}
  .foot__cols{grid-template-columns:1fr;gap:var(--spacing-40)}
  .enquiry{padding:var(--spacing-32) var(--spacing-24)}
  .callout,.lifted,.grid2 > .card{padding:var(--spacing-32)}
  .site-footer{padding-top:var(--spacing-80)}
  .js .thumbbar{display:block}
  .js body{padding-bottom:84px}
}
@media (max-width:479px){
  /* Never below 32px, even here - the breathing room is the brand. */
  :root{--section-gap:var(--spacing-48);--page-padding:var(--spacing-24)}
  .logo__mark{width:158px}
  .band--tight{padding:var(--spacing-40) 0}
  .lifted,.callout,.enquiry{padding:var(--spacing-24)}
  .btn{width:100%}
  .btn-row{gap:var(--spacing-12)}
}

/* ============================================================
   PRINT
   ============================================================ */
@media print{
  .site-header,.thumbbar,.skip,.btn,.gap,.imageslot{display:none !important}
  body{background:#fff;color:#000;font-size:11pt}
  .band,.band--tight,.hero{padding:16pt 0}
  .site-footer{background:#fff;color:#000;border-top:1px solid #999;padding:16pt 0}
  .site-footer a,.legal{color:#000}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:.85em}
  .faq__a{display:block !important}
}

/* ============================================================
   SPACING AND HEADING UTILITIES
   A small, closed set drawn from the Avenir scale. The prototype
   scattered inline styles for these; a fixed scale stops the next
   person inventing a fourteenth margin value.
   ============================================================ */
.u-mt-s{margin-top:var(--spacing-20)}
.u-mt-m{margin-top:var(--spacing-24)}
.u-mt-l{margin-top:var(--spacing-40)}
.u-mt-xl{margin-top:var(--spacing-48)}
.u-mb-m{margin-bottom:var(--spacing-24)}
.u-mb-l{margin-bottom:var(--spacing-48)}
.u-h4{font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm)}
.u-h5{font-size:var(--text-body-lg);line-height:var(--leading-body-lg);letter-spacing:-.02em}

/* ============================================================
   TOOL PAGES
   These are the pages that do the selling, so they invert: deep navy
   ground, a sticky rail holding the tool's name and contents, and a
   single flowing column beside it. Everything else on the site sits on
   parchment, so a tool page announces itself as a different kind of
   page before a word is read.

   Avenir calls for this shape on long-form pages - a sticky sidebar of
   mono anchors beside a flowing column - so the treatment is the
   system's own rather than an invention.
   ============================================================ */
/* The whole page, not just the article. .tool sits inside <main>,
   which starts below the header - so a transparent bar at the top of
   the page revealed the parchment body behind it, and the header
   looked white until you scrolled. Inverting the body fixes that and
   carries the dark ground down into the footer. */
body.page--tool{background:var(--surface-slate-teal)}
.tool{background:var(--surface-slate-teal);color:var(--ink-reverse)}
.tool h1,.tool h2,.tool h3{color:var(--ink-reverse)}
.tool a{color:var(--ink-reverse)}
.tool a:hover{color:var(--color-mist-blue)}
.tool .lede,.tool .muted{color:var(--ink-reverse-muted)}
.tool .eyebrow{color:var(--color-mist-blue)}
.tool :focus-visible{outline-color:var(--color-signal-mint)}
/* The status tag has to restate itself on a navy ground, or the navy
   fill and navy border vanish into it. */
.tool .pill--soon{background:transparent;color:var(--ink-reverse);border-color:var(--color-warm-parchment)}
/* Each button variant restates its ink: .tool a is more specific. */
.tool .btn--primary,.tool .btn--primary:hover{color:var(--color-deep-navy)}
.tool .btn--ghost{color:var(--ink-reverse);border-color:var(--color-warm-parchment)}
.tool .btn--ghost:hover{background:var(--color-mist-blue);color:var(--color-deep-navy);border-color:var(--color-mist-blue)}

.tool__masthead{padding:var(--spacing-80) 0 var(--spacing-64)}
/* A rule between the masthead and the body, held to the page gutter
   rather than run edge to edge: on a full-bleed navy page the two
   halves otherwise dissolve into one another. */
/* Parchment, not mint, in the masthead alone. The accent is the
   primary button, and here the pill sits a few lines above one: two
   mint blocks in the same eye-line and the tag competes with the
   thing you are meant to press. Everywhere else - on the tools index,
   on a card - nothing else is mint nearby and the accent does its job
   of finding the eye. */
.tool__masthead .pill--now{background:var(--color-warm-parchment);color:var(--color-deep-navy);border-color:var(--color-warm-parchment)}
.tool__masthead .wrap{border-bottom:1px solid var(--rule-reverse);padding-bottom:var(--spacing-64)}
.tool__masthead h1{margin-top:var(--spacing-16);max-width:18ch}
.tool__masthead .lede{max-width:52ch}
.tool__masthead .pill{margin-bottom:var(--spacing-16)}
/* An aside to the pitch above it, so it is set as one: the real Lora
   italic rather than a synthesised slope, and a step fainter than the
   surrounding reverse ink. Parchment at 68% over the navy is 6.9:1,
   still well clear of the 4.5:1 minimum for body text. */
.tool__note{font-family:var(--font-lora-editorial);font-style:italic;
  color:#acb6ba;max-width:56ch;margin:var(--spacing-40) 0 0}

.tool__body{padding-bottom:var(--spacing-120)}
/* No align-items:start here. Starting the aside collapses it to its
   own content height, and a sticky child cannot travel further than
   its parent - the rail would scroll away with the page. Letting the
   column stretch to the row gives the sticky inner somewhere to go. */
/* Three columns for two children. The rail sits in the first, at the
   page gutter where the masthead starts; the flowing column sits in
   the second; the third is left empty on purpose, mirroring the rail's
   width so the reading column lands on the centre of the page rather
   than merely on the centre of what is left over. No spacer element
   needed - grid simply does not fill the last track. */
.tool__grid{display:grid;
  grid-template-columns:minmax(0,16rem) minmax(0,1fr) minmax(0,16rem);
  gap:var(--spacing-64)}
.rail{align-self:stretch}

/* ---- the sticky rail ---- */
.rail__inner{position:sticky;top:calc(var(--header-h) + var(--spacing-32))}
.rail__eyebrow{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);
  font-size:var(--text-caption);line-height:var(--leading-caption);letter-spacing:var(--tracking-caption);
  text-transform:uppercase;margin:0 0 var(--spacing-20)}
.rail__eyebrow a{color:var(--color-mist-blue);text-decoration:none;display:inline-flex;align-items:center;min-height:44px}
.rail__eyebrow a:hover{text-decoration:underline;text-underline-offset:4px}
.rail__name{font-family:var(--font-lora-editorial);font-size:var(--text-heading-sm);
  line-height:var(--leading-heading-sm);letter-spacing:var(--tracking-heading-sm);
  color:var(--ink-reverse);margin:0}
/* The rail carries the tool's name, so the sticky column still says
   which tool you are reading once the masthead has scrolled away.
   "On this page" drops to a mono label over the list it introduces. */
.rail__label{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);
  font-size:var(--text-caption);line-height:var(--leading-caption);
  letter-spacing:var(--tracking-caption);text-transform:uppercase;
  color:var(--ink-reverse-muted);margin:var(--spacing-32) 0 0}
.rail__nav{list-style:none;counter-reset:r;padding:0;margin:var(--spacing-12) 0 0;
  border-top:1px solid var(--rule-reverse)}
.rail__nav li{counter-increment:r;border-bottom:1px solid var(--rule-reverse)}
.rail__nav a{display:flex;align-items:center;gap:var(--spacing-12);min-height:46px;
  text-decoration:none;color:var(--ink-reverse-muted);
  font-size:var(--text-body-sm);letter-spacing:var(--tracking-body-sm)}
.rail__nav a::before{content:counter(r,decimal-leading-zero);
  font-family:var(--font-fragment-mono-label);font-size:var(--text-caption);
  letter-spacing:var(--tracking-caption);color:var(--color-mist-blue);opacity:.7}
.rail__nav a:hover{color:var(--ink-reverse)}
.rail__nav a[aria-current]{color:var(--ink-reverse);font-weight:var(--font-weight-medium)}
.rail__nav a[aria-current]::before{color:var(--color-signal-mint);opacity:1}
.rail__cta{margin:var(--spacing-32) 0 0}
/* Full width in a narrow column, so the button's own generous side
   padding is what pushes "Request a demonstration" onto a second line.
   Trimming it here gives the label room without changing the button
   anywhere else on the site. */
.rail__cta .btn{width:100%;padding-inline:var(--spacing-16)}

/* ---- the flowing column ---- */
.flow{max-width:820px;margin-inline:auto}
.flow__section{padding-top:var(--spacing-64);scroll-margin-top:calc(var(--header-h) + var(--spacing-24))}
.flow__section:first-child{padding-top:0}
.flow__section + .flow__section{border-top:1px solid var(--rule-reverse);margin-top:var(--spacing-64)}
.flow__section h2{font-size:var(--text-heading);line-height:var(--leading-heading);letter-spacing:var(--tracking-heading);margin-bottom:var(--spacing-24)}
.flow__lead{font-size:var(--text-lede);line-height:var(--leading-lede)}
.flow__list{list-style:none;padding:0;margin:0}
.flow__list li{position:relative;padding-left:var(--spacing-24);margin-bottom:var(--spacing-16)}
.flow__list li::before{content:"";position:absolute;left:0;top:.68em;width:8px;height:1px;background:var(--color-signal-mint)}
.tool .figure{background:rgba(247,246,245,.06)}
.tool .figure svg{color:var(--color-warm-parchment)}
.tool .figure figcaption{color:var(--ink-reverse-muted)}
.tool .gap{border-color:var(--rule-reverse);background:rgba(247,246,245,.05);color:var(--ink-reverse-muted)}
.tool .gap__head{color:var(--ink-reverse)}
.tool .gap code{font-family:var(--font-fragment-mono-label);font-size:.9em;color:var(--color-mist-blue)}
/* A light card inside the inverted page restates its own ink, the same
   way it has to inside .band--dark. Without this the card title and the
   status tag inherit the page's parchment and vanish on white. */
.tool .card,.tool .toolcard{color:var(--ink)}
.tool .card h2,.tool .card h3,.tool .card a,
.tool .toolcard h3,.tool .toolcard a{color:var(--ink)}
.tool .card a:hover,.tool .toolcard a:hover{color:var(--ink-muted)}
.tool .toolcard__list{color:var(--ink-muted)}
.tool .toolcard .pill--soon{background:transparent;color:var(--ink);border-color:var(--color-deep-navy)}
.tool .callout{background:rgba(247,246,245,.07)}
.tool .callout__title{color:var(--ink-reverse)}
.tool__close{padding-bottom:var(--spacing-64)}

/* ---- screenshots ---- */
.shots{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--element-gap);margin:var(--spacing-32) 0}
.shots .shot:first-child{grid-column:1 / -1}
.shot{margin:0}
.shot__art{display:block;width:100%;height:auto;border-radius:var(--radius-cards);
  background:rgba(247,246,245,.06);color:var(--color-warm-parchment)}
.shot__open{display:block;border-radius:var(--radius-cards);
  transition:transform var(--duration-card-hover) var(--easing-card-hover)}
.shot__open:hover{transform:translateY(-4px)}
.shot figcaption{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);
  font-size:var(--text-caption);line-height:1.5;letter-spacing:var(--tracking-caption);
  text-transform:uppercase;color:var(--ink-reverse-muted);margin-top:var(--spacing-12)}

/* ---- the recorded demonstration ---- */
.demo{margin:var(--spacing-32) 0}
.demo__player{width:100%;height:auto;display:block;border-radius:var(--radius-cards);background:#000}
.demo figcaption{font-family:var(--font-fragment-mono-label);font-weight:var(--font-weight-medium);
  font-size:var(--text-caption);line-height:1.5;letter-spacing:var(--tracking-caption);
  text-transform:uppercase;color:var(--ink-reverse-muted);margin-top:var(--spacing-12)}

/* ---- lightbox ----
   A native <dialog>, so focus trapping, Escape and inert background
   come from the browser rather than from hand-written script. Every
   thumbnail is an ordinary link to the full image underneath, so with
   JavaScript off the picture still opens. */
/* A panel over the page rather than a takeover of it: 90% of the
   viewport, centred by the dialog's own auto margins, with the page
   still visible behind the backdrop so it reads as a modal. */
.lightbox{border:0;padding:0;width:90vw;height:90vh;max-width:90vw;max-height:90vh;
  border-radius:var(--radius-large);overflow:hidden;
  background:var(--color-deep-navy);color:var(--ink-reverse);
  box-shadow:0 24px 60px rgba(0,0,0,.45)}
.lightbox::backdrop{background:rgba(11,46,60,.82);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.lightbox__inner{display:flex;flex-direction:column;height:100%;padding:var(--spacing-24);gap:var(--spacing-16)}
.lightbox__bar{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-16);flex-wrap:wrap}
.lightbox__controls{display:flex;gap:var(--spacing-8)}
.lightbox__count{font-family:var(--font-fragment-mono-label);font-size:var(--text-caption);
  letter-spacing:var(--tracking-caption);text-transform:uppercase;color:var(--color-mist-blue)}
.lightbox__stage{flex:1;display:grid;place-items:center;min-height:0}
.lightbox__stage img,.lightbox__stage svg{max-width:100%;max-height:100%;width:auto;height:auto;
  border-radius:var(--radius-cards);color:var(--color-warm-parchment)}
.lightbox__cap{text-align:center;font-size:var(--text-body-sm);color:var(--ink-reverse-muted);margin:0}
.lightbox__btn{background:transparent;border:1px solid var(--color-warm-parchment);color:var(--ink-reverse);
  border-radius:var(--radius-buttons);min-height:44px;min-width:44px;padding:0 var(--spacing-16);
  font:inherit;font-size:var(--text-body-sm);cursor:pointer}
.lightbox__btn:hover{background:var(--color-mist-blue);color:var(--color-deep-navy);border-color:var(--color-mist-blue)}
.lightbox :focus-visible{outline:3px solid var(--color-signal-mint);outline-offset:3px}

/* The rail collapses well before the site's lg breakpoint. Three
   columns need room the tool page does not have on a small laptop:
   at 1024 the reading column was down to 256px, narrower than the
   rail beside it. From 1280 the rail earns its place; below that the
   contents belong in a band over the text, and the reading column
   gets the full 820px instead. */
@media (max-width:1279px){
  .tool__grid{grid-template-columns:1fr;gap:var(--spacing-48)}
  .flow{margin-inline:0}
  /* The rail stops sticking once it is a horizontal band above the
     content - a sticky element that spans the viewport just eats it. */
  /* Held to the same 820px column as the flow below it, so the
     contents band and the reading column share one left edge. */
  .rail__inner{position:static;max-width:820px;margin-inline:auto}
  /* Nothing sticks here, so the h1 is still on screen - repeating the
     tool's name directly under it would just be an echo. */
  .rail__name{display:none}
  .rail__nav{display:flex;flex-wrap:wrap;gap:0 var(--spacing-24);border-bottom:1px solid var(--rule-reverse)}
  .rail__nav li{border-bottom:0}
  .rail__cta{display:none}
  .flow{max-width:820px;margin-inline:auto}
}
@media (max-width:767px){
  .shots{grid-template-columns:1fr}
  .shots .shot:first-child{grid-column:auto}
  .tool__body{padding-bottom:var(--spacing-80)}
  .tool__close{padding-bottom:var(--spacing-80)}
}

/* ---- the hero wash ----
   A grain gradient, baked to a still rather than run live. The look
   came from a WebGL component; shipping that component would have put
   React and a shader runtime - two thirds of a megabyte - on a site
   whose entire argument is that it loads nothing from anywhere, and
   put a second perpetual animation in a hero that already carries
   one. Rendered once at build time, it costs 39kB and no script.

   It is pulled up behind the header, which is transparent until you
   scroll, so the wash starts at the top of the window rather than
   under the bar. Two plates: the wave arrives from the top right, and
   a landscape plate stretched over a phone loses that corner. */
.wash{position:relative;isolation:isolate}
/* The opening block of a page can be a hero in its own right: one
   viewport tall, with the logo strip pinned to its floor by the auto
   margin the strip already carries. Below that height it simply grows,
   so a long biography is never clipped or crushed to fit. */
.wash--hero{display:flex;flex-direction:column;
  min-height:100vh;min-height:calc(100svh - var(--header-h))}
/* .wrap centres itself with auto side margins, and an auto margin on
   the cross axis of a flex container cancels the stretch it would
   otherwise get. Left alone the page head shrank to its own text and
   sat 230px from the edge while every other container was at 64. */
.wash--hero > *{width:100%}
/* The band inside a hero does not need its own closing space: the
   strip below it brings its own, and the block is trying to fit one
   screen. */
.wash--hero > .band{padding-bottom:var(--spacing-32)}
/* Unless it is the last thing in the wash. On /about/ the logo strip
   follows and brings its own space; on /consultancy/ the band runs
   straight into the dark section below it, and 32px there reads as a
   collision rather than a seam. */
.wash--hero > .band:last-child{padding-bottom:var(--spacing-80)}
/* The plate is a real element rather than a pseudo-element, because a
   pseudo-element cannot host a canvas. It carries the baked still; the
   shader, when it runs, paints its canvas over that still inside this
   same box, so what happens if WebGL is missing is simply nothing. */
.wash__plate{position:absolute;z-index:-1;pointer-events:none;isolation:isolate;
  left:0;right:0;bottom:0;top:calc(var(--header-h) * -1);
  background:url("/assets/img/hero-wash-wide.webp") top center/cover no-repeat;
  /* Faded out at the foot, or the plate ends in a straight horizontal
     cut across the page where the wash meets the parchment below. */
  -webkit-mask-image:linear-gradient(to bottom,#000 58%,transparent 96%);
          mask-image:linear-gradient(to bottom,#000 58%,transparent 96%)}
/* The library injects these rules itself, in a <style> element it
   creates at runtime. This site's Content Security Policy allows no
   inline styles, so that element is dropped and the canvas would be
   left unpositioned. Written here instead, where the policy allows
   them, so the shader is styled by the site rather than by its own
   script. */
[data-paper-shader] canvas{contain:strict;display:block;position:absolute;
  inset:0;z-index:-1;width:100%;height:100%;border-radius:inherit}

@media (max-width:767px){
  /* No wash on a phone. Anchored to the plate's right edge it was a
     flat tint rather than a wave arriving into a corner, and the
     parchment the brand sits on is the better ground at that size. */
  .wash__plate{display:none}
  /* The hero card takes the same padding as every other card once
     there is one column: at 40px it was inset further from the screen
     edge than the tool cards below it, which reads as a different
     kind of thing rather than the same thing higher up. */
  .hero__aside{padding:var(--card-padding);border-width:6px}
}

/* ============================================================
   HERO STRIP
   The hero fills the viewport and the logo strip sits on its floor,
   so the first screen is the proposition and the proof of it.
   ============================================================ */
/* The vertical rhythm comes from centring inside the viewport now, so
   the old fixed top padding would push the strip past the fold. */
.hero{display:flex;flex-direction:column;min-height:100vh;min-height:calc(100svh - var(--header-h));
  padding:var(--spacing-40) 0 0}
.hero > .wrap{flex:1;display:flex;flex-direction:column;justify-content:center}
/* margin-top:auto pins the strip to the floor of the hero, so the
   bottom padding is the only thing standing between the logos and the
   fold. It needs to be generous or they read as cut off by it. */
.marquee{padding:var(--spacing-40) 0;margin-top:auto}
/* Only in the hero does the strip need clearance beneath it: there it
   is pinned to the floor of the viewport and the padding is all that
   stands between the logos and the fold. Everywhere else it is one
   band among several and takes ordinary spacing. */
.hero .marquee{padding-bottom:var(--spacing-80)}
.marquee__head{margin:0}
.marquee__head .label{margin:0}
/* The strip starts and stops on the page gutter now, like everything
   else, rather than running to the edge of the screen. The fade is a
   fixed distance rather than a percentage: as a percentage it grew
   with the viewport, so on a wide screen the first whole logo did not
   appear until a long way in. */
.marquee__viewport{overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 72px,#000 calc(100% - 72px),transparent);
          mask-image:linear-gradient(90deg,transparent,#000 72px,#000 calc(100% - 72px),transparent)}
/* Without JavaScript nothing moves, so the strip becomes an ordinary
   scrollable row instead - no motion means nothing to pause. */
html:not(.js) .marquee__viewport{overflow-x:auto}
.marquee__track{display:flex;width:max-content;list-style:none;margin:0;padding:0;align-items:center}
/* Margin rather than gap: the loop translates exactly half the track,
   which only lands cleanly if both runs have identical box widths. */
/* 48px, not 80. With 27 marks on the track every pixel between them
   is 27 pixels of cycle, and a logo at the far end was more than two
   minutes from coming round. */
.marquee__item{margin-right:var(--spacing-48);color:var(--ink);flex:none}
/* One height for every logo, its own width. Colour is dropped and the
   whole strip sits back a little: these are other organisations' marks
   on someone else's page, and at full strength a row of them competes
   with the headline it sits under. */
/* Size comes from the width and height attributes, worked out at build
   time from each file's own dimensions, so the strip reserves its
   space before a single logo has loaded. Colour is dropped and the
   whole row sits back a little: these are other organisations' marks
   on someone else's page, and at full strength a row of them competes
   with the headline it sits under. */
.marquee__art{display:block;max-width:none;
  filter:grayscale(1);opacity:.72;
  /* Logos exported on a white card would otherwise sit in visible white
     tiles on the parchment. Multiply drops the white into the ground
     and leaves a transparent export untouched. */
  mix-blend-mode:multiply}
@media (forced-colors:active){
  .marquee__art{filter:none;opacity:1;mix-blend-mode:normal}
}
/* 48s is the fallback. With the real set the script replaces it with a
   duration taken from the track's own width, so the logos travel at one
   speed whatever the count. It runs continuously and has no pause
   control - see the accessibility statement, which records that. */
.js .marquee__track{animation:marquee-loop 48s linear infinite}
@keyframes marquee-loop{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){
  .js .marquee__track{animation:none}
  .js .marquee__viewport{overflow-x:auto}
}
@media (max-width:767px){
  .hero{min-height:0}
  .marquee__item{margin-right:var(--spacing-32)}
}


/* Pricing. The figure sits in its own column on the pricing page and
   as a bold lead-in on a tool page; the early adopter line beneath it
   is quieter, so the full price stays the thing you read first. */
.prose td.price{white-space:nowrap;font-weight:var(--font-weight-medium)}
.price__offer{display:inline-block;font-size:var(--text-caption);letter-spacing:var(--tracking-caption);color:var(--ink-muted)}
