/* ==========================================================================
   Virtual CFO — design system
   Palette inherited from the existing site; nothing invented.
   ========================================================================== */

:root{
  /* Brand — sampled from the live site and logo */
  --navy:        #163651;
  --navy-deep:   #0E2438;
  --navy-soft:   #24557E;
  --cyan:        #00A5DF;
  --cyan-deep:   #0B87B8;
  --pale:        #C5D8EB;
  --slate:       #7E9AB5;
  --ink:         #171717;
  --paper:       #FFFFFF;
  --paper-2:     #F7F9FB;

  /* Derived surfaces */
  --rule:        rgba(22,54,81,.14);
  --rule-strong: rgba(22,54,81,.28);
  --rule-invert: rgba(197,216,235,.22);

  /* Type */
  --display: "Montserrat", "Segoe UI", Arial, sans-serif;
  --body:    "PT Sans", "Segoe UI", Arial, sans-serif;

  /* Rhythm */
  --gutter:  clamp(1.25rem, 4vw, 2.5rem);
  --section: clamp(5.5rem, 10vw, 9rem);
  --maxw:    1200px;

  /* Motion — decelerating curves. Things arrive and settle; they never bounce. */
  --ease:      cubic-bezier(.22,.61,.36,1);
  --ease-out:  cubic-bezier(.16,1,.3,1);
  --ease-soft: cubic-bezier(.4,0,.2,1);
  --d-fast:   .25s;
  --d-mid:    .55s;
  --d-slow:   .9s;

  /* Radii — continuous, generous. */
  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;

  /* Depth — layered, low-opacity. One shadow reads as a sticker; three read as air. */
  --shadow-sm:
    0 1px 2px rgba(14,36,56,.05),
    0 2px 8px rgba(14,36,56,.04);
  --shadow-md:
    0 2px 4px rgba(14,36,56,.04),
    0 8px 20px rgba(14,36,56,.06),
    0 20px 40px rgba(14,36,56,.05);
  --shadow-lg:
    0 4px 10px rgba(14,36,56,.06),
    0 18px 40px rgba(14,36,56,.10),
    0 40px 80px -20px rgba(14,36,56,.22);
}

/* --- reset ---------------------------------------------------------------- */
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--body);
  font-size:1.0625rem;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
}
img,svg{ max-width:100%; display:block; }
a{ color:inherit; }
button{ font:inherit; color:inherit; }

/* --- type ----------------------------------------------------------------- */
h1,h2,h3,h4{
  font-family:var(--display);
  color:var(--navy);
  margin:0;
  text-wrap:balance;
}
h1{ font-size:clamp(2.5rem,5.6vw,4.25rem); font-weight:800; letter-spacing:-.032em; line-height:1.04; }
h2{ font-size:clamp(1.85rem,3.4vw,2.75rem); font-weight:700; letter-spacing:-.022em; line-height:1.12; }
h3{ font-size:1.1875rem; font-weight:700; letter-spacing:-.01em; line-height:1.3; }
p{ margin:0 0 1.1em; }
p:last-child{ margin-bottom:0; }

.eyebrow{
  font-family:var(--display);
  font-size:.7rem;
  font-weight:700;
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--cyan-deep);
  margin:0 0 1.25rem;
  display:flex;
  align-items:center;
  gap:.7rem;
}
.eyebrow::after{ content:""; flex:1; height:1px; background:var(--rule); max-width:5rem; }
.lede{ font-size:1.1875rem; color:#3B4A57; max-width:56ch; }

/* --- layout --------------------------------------------------------------- */
.wrap{ width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter); }
.section{ padding-block:var(--section); }
.section--tint{ background:var(--paper-2); }
.section__head{ max-width:60ch; margin-bottom:clamp(2.5rem,5vw,3.75rem); }

/* --- buttons -------------------------------------------------------------- */
.btn{
  --btn-bg:var(--cyan);
  --btn-fg:#fff;
  display:inline-flex;
  align-items:center;
  gap:.6rem;
  padding:.85rem 1.6rem;
  background:var(--btn-bg);
  color:var(--btn-fg);
  font-family:var(--display);
  font-size:.875rem;
  font-weight:700;
  letter-spacing:.01em;
  text-decoration:none;
  border:1px solid var(--btn-bg);
  border-radius:var(--r-sm);
  cursor:pointer;
  will-change:transform;
  transition:background var(--d-fast) var(--ease-soft),
             border-color var(--d-fast) var(--ease-soft),
             box-shadow var(--d-mid) var(--ease-out),
             transform var(--d-mid) var(--ease-out);
}
.btn:hover{ --btn-bg:var(--cyan-deep); transform:translateY(-1px); box-shadow:var(--shadow-sm); }
.btn:active{ transform:translateY(0) scale(.978); transition-duration:.09s; }
.btn--ghost{ --btn-bg:transparent; --btn-fg:var(--navy); border-color:var(--rule-strong); }
.btn--ghost:hover{ --btn-bg:transparent; --btn-fg:var(--cyan-deep); border-color:var(--cyan); }
.btn--onDark{ --btn-bg:transparent; --btn-fg:#fff; border-color:var(--rule-invert); }
.btn--onDark:hover{ --btn-bg:rgba(255,255,255,.08); border-color:var(--pale); }

.btn__arrow{ transition:transform var(--d-mid) var(--ease-out); }
.btn:hover .btn__arrow{ transform:translateX(4px); }

/* --- focus ---------------------------------------------------------------- */
:focus-visible{ outline:2px solid var(--cyan); outline-offset:3px; border-radius:var(--r-sm); }

.skip{
  position:absolute; left:-9999px; top:0; z-index:100;
  background:var(--navy); color:#fff; padding:.75rem 1.25rem; text-decoration:none;
}
.skip:focus{ left:.5rem; top:.5rem; }

/* --- header --------------------------------------------------------------- */
.site-header{
  position:sticky; top:0; z-index:50;
  background:rgba(255,255,255,.72);
  backdrop-filter:saturate(180%) blur(20px);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  border-bottom:1px solid transparent;
  transition:background var(--d-mid) var(--ease-soft),
             border-color var(--d-mid) var(--ease-soft),
             box-shadow var(--d-mid) var(--ease-soft);
}
.site-header[data-stuck="true"]{
  background:rgba(255,255,255,.82);
  border-bottom-color:var(--rule);
  box-shadow:0 1px 24px rgba(22,54,81,.07);
}
.site-header__inner{
  display:flex; align-items:center; gap:2rem;
  height:80px;
  transition:height var(--d-mid) var(--ease-out);
}
.site-header[data-stuck="true"] .site-header__inner{ height:64px; }
.brand{ display:flex; align-items:center; flex:none; }
.brand svg{ height:30px; width:auto; }

.nav{ display:flex; align-items:center; gap:1.9rem; margin-left:auto; }
.nav__link{
  position:relative;
  font-family:var(--display); font-size:.875rem; font-weight:600;
  color:var(--navy); text-decoration:none; padding-block:.4rem;
  background:none; border:0; cursor:pointer;
}
.nav__link::after{
  content:""; position:absolute; left:0; right:0; bottom:0;
  height:2px; background:var(--cyan);
  transform:scaleX(0); transform-origin:left;
  transition:transform var(--d-mid) var(--ease-out);
}
.nav__link:hover::after,
.nav__link[aria-expanded="true"]::after{ transform:scaleX(1); }

.nav__toggle{ display:none; }

/* mega menu */
.mega{
  position:absolute; left:0; right:0; top:100%;
  background:var(--paper);
  border-bottom:1px solid var(--rule);
  box-shadow:var(--shadow-md);
  opacity:0; visibility:hidden; transform:translateY(-10px) scale(.99);
  transform-origin:top center;
  transition:opacity var(--d-fast) var(--ease-soft),
             transform var(--d-mid) var(--ease-out),
             visibility var(--d-mid);
}
.mega[data-open="true"]{ opacity:1; visibility:visible; transform:translateY(0) scale(1); }
.mega__grid{
  display:grid; grid-template-columns:repeat(3,1fr); gap:2.5rem;
  padding-block:2.5rem;
}
.mega__label{
  font-family:var(--display); font-size:.68rem; font-weight:700;
  letter-spacing:.18em; text-transform:uppercase; color:var(--slate);
  padding-bottom:.75rem; margin-bottom:.75rem; border-bottom:1px solid var(--rule);
}
.mega__list{ list-style:none; margin:0; padding:0; display:grid; gap:.15rem; }
.mega__list a{
  display:block; padding:.4rem 0; font-size:.9375rem;
  color:var(--ink); text-decoration:none;
  border-radius:var(--r-sm);
  /* padding-left here laid out the whole mega panel on hover; the same
     movement is a transform in motion layer 3. */
  transition:color var(--d-fast) var(--ease-soft),
             background var(--d-fast) var(--ease-soft),
             transform var(--d-mid) var(--ease-out);
}
.mega__list a:hover{ color:var(--cyan-deep); background:rgba(0,165,223,.05); transform:translateX(.6rem); }

/* --- hero ----------------------------------------------------------------- */
.hero{ padding-block:clamp(3.5rem,7vw,6rem) clamp(4rem,8vw,7rem); overflow:hidden; }
.hero__grid{
  display:grid; grid-template-columns:1.05fr .95fr;
  gap:clamp(2.5rem,5vw,4.5rem); align-items:center;
}
.hero__actions{ display:flex; flex-wrap:wrap; gap:.85rem; margin-top:2.25rem; }
.hero__note{
  margin-top:2.25rem; padding-top:1.25rem; border-top:1px solid var(--rule);
  font-size:.9375rem; color:var(--slate);
}
.hero__note strong{ color:var(--navy); font-weight:400; }

/* --- signature: coverage checker ------------------------------------------ */
.panel{
  background:var(--navy);
  color:#fff;
  border-radius:var(--r-lg);
  padding:clamp(1.75rem,3vw,2.4rem);
  box-shadow:var(--shadow-lg);
}
.panel__q{
  font-family:var(--display); font-size:.68rem; font-weight:700;
  letter-spacing:.18em; text-transform:uppercase;
  color:var(--pale); opacity:.7; margin:0 0 .9rem;
}
.panel__pills{ display:flex; flex-wrap:wrap; gap:.4rem; margin-bottom:1.6rem; }
.panel__pill{
  background:rgba(255,255,255,.07); border:1px solid transparent;
  color:rgba(255,255,255,.7);
  padding:.5rem .9rem; border-radius:999px; cursor:pointer;
  font-family:var(--display); font-size:.8125rem; font-weight:600;
  transition:background var(--d-fast) var(--ease-soft),
             color var(--d-fast) var(--ease-soft),
             border-color var(--d-fast) var(--ease-soft),
             transform var(--d-mid) var(--ease-out);
}
.panel__pill:active{ transform:scale(.96); transition-duration:.09s; }
.panel__pill:hover{ color:#fff; background:rgba(255,255,255,.13); }
.panel__pill[aria-pressed="true"]{ background:var(--cyan); color:#fff; border-color:var(--cyan); }

.panel__block + .panel__block{ margin-top:1.5rem; padding-top:1.35rem; border-top:1px solid var(--rule-invert); }
.panel__blockhead{ display:flex; align-items:baseline; gap:.75rem; margin-bottom:.7rem; }
.panel__blockhead h3{ font-size:.9375rem; color:#fff; font-weight:700; margin:0; }
.panel__tag{
  margin-left:auto; font-family:var(--display); font-size:.66rem; font-weight:700;
  letter-spacing:.12em; text-transform:uppercase; padding:.25rem .6rem; border-radius:999px;
}
.panel__tag[data-state="yes"]{ background:rgba(0,165,223,.22); color:#8FDCFB; }
.panel__tag[data-state="soon"]{ background:rgba(255,255,255,.12); color:var(--pale); }
.panel__tag[data-state="no"]{ background:rgba(255,255,255,.08); color:var(--slate); }

.panel__list{ list-style:none; margin:0; padding:0; }
.panel__row{
  display:flex; align-items:flex-start; gap:.65rem;
  padding:.36rem 0; font-size:.9rem; color:rgba(255,255,255,.9);
  transition:opacity .2s var(--ease), transform .2s var(--ease);
}
.panel__row svg{ flex:none; margin-top:.32em; }
.panel__note{ font-size:.875rem; color:var(--pale); margin:.55rem 0 0; }

/* Anchor the panel to the top of the hero row. Coverage states differ in height,
   and centring would move the pills out from under the cursor on every click. */
@media (min-width:961px){
  .panel{ align-self:start; }
}

/* --- service groups ------------------------------------------------------- */
.svc-group + .svc-group{ margin-top:clamp(3rem,5vw,4.5rem); }
.svc-group__head{
  display:flex; align-items:baseline; gap:1rem;
  padding-bottom:1rem; margin-bottom:1.75rem;
  border-bottom:2px solid var(--navy);
}
.svc-group__head h3{ font-size:1.375rem; }
.svc-group__meta{
  margin-left:auto; font-size:.8125rem; color:var(--slate);
  font-family:var(--display); font-weight:600; letter-spacing:.04em;
}
/* The service index.
   Was a two-column grid of narrow name+blurb cells. Fourteen services in
   narrow cells read as a directory of equal-weight options; as full-width
   numbered rows they read as an index, which is what a list of fourteen
   things actually is. Each row carries a margin numeral, the name, and the
   blurb in its own column, and the whole row is the link. */
.svc-list{ list-style:none; margin:0; padding:0; }

.svc-item{ position:relative; border-bottom:1px solid var(--rule); }
.svc-item a,
.svc-item__static{
  position:relative; z-index:1;
  display:grid; gap:.3rem 1.5rem;
  grid-template-columns:2.5rem minmax(0,1fr);
  padding:1.3rem .9rem 1.3rem 0;
  text-decoration:none;
  transition:padding-left var(--d-mid) var(--ease-out);
}
@media (min-width:860px){
  .svc-item a,
  .svc-item__static{
    grid-template-columns:3.25rem minmax(0,1fr) minmax(0,1.1fr);
    align-items:baseline;
  }
}

.svc-item__n{
  font-family:var(--display); font-weight:700; font-size:.7rem;
  letter-spacing:.1em; color:var(--slate); padding-top:.35rem;
  font-variant-numeric:tabular-nums;
  transition:color var(--d-fast) var(--ease-soft);
}
.svc-item__name{
  font-family:var(--display); font-weight:700;
  font-size:clamp(1.0625rem,1.7vw,1.3125rem); line-height:1.22;
  color:var(--navy);
  transition:color var(--d-fast) var(--ease-soft);
}
.svc-item__desc{
  grid-column:2/-1; margin:0;
  font-size:.9375rem; color:#54636F; max-width:52ch;
  transition:color var(--d-fast) var(--ease-soft);
}
@media (min-width:860px){
  .svc-item__desc{ grid-column:3; }
}

/* The mark. A navy field wipes across the row from the left, behind the
   content rather than over it, so nothing reflows and the row keeps one
   compositor layer for the whole gesture. */
.svc-item__fill{
  position:absolute; inset:0; z-index:0;
  background:var(--navy);
  transform:scaleX(0); transform-origin:left;
  transition:transform var(--d-mid) var(--ease-out);
}
.svc-item:hover .svc-item__fill,
.svc-item:focus-within .svc-item__fill{ transform:scaleX(1); }

/* Every colour change is scoped through the child anchor, so the static rows
   on the Insights page - which carry no link and no fill - are never told to
   turn their text white against a background that stayed pale. */
.svc-item:hover > a,
.svc-item:focus-within > a{ padding-left:1.1rem; }
.svc-item:hover > a .svc-item__name,
.svc-item:focus-within > a .svc-item__name{ color:#fff; }
.svc-item:hover > a .svc-item__desc,
.svc-item:focus-within > a .svc-item__desc{ color:var(--pale); }
.svc-item:hover > a .svc-item__n,
.svc-item:focus-within > a .svc-item__n{ color:var(--cyan); }

.svc-item__go{
  position:absolute; right:.9rem; top:50%; z-index:1;
  color:var(--cyan); font-size:.9375rem; line-height:1;
  transform:translate(-.6rem,-50%); opacity:0;
  transition:transform var(--d-mid) var(--ease-out), opacity var(--d-fast) var(--ease-soft);
}
.svc-item:hover .svc-item__go,
.svc-item:focus-within .svc-item__go{ transform:translate(0,-50%); opacity:1; }
@media (max-width:859px){ .svc-item__go{ display:none; } }

/* --- split feature (UAE desk) --------------------------------------------- */
.split{
  display:grid; grid-template-columns:1fr 1fr;
  gap:clamp(2.5rem,5vw,4.5rem); align-items:start;
}
.office{
  padding:1.5rem 0;
  border-top:1px solid var(--rule);
}
.office__name{
  font-family:var(--display); font-weight:700; font-size:1rem;
  color:var(--navy); margin:0 0 .4rem;
}
.office__body{ font-size:.9375rem; color:#54636F; margin:0; }
.office__body a{ color:var(--cyan-deep); text-underline-offset:3px; }

.credential{
  display:grid; gap:.35rem;
  padding:1.25rem 0; border-top:1px solid var(--rule);
}
.credential__val{
  font-family:var(--display); font-weight:800; font-size:2rem;
  color:var(--navy); letter-spacing:-.02em; font-variant-numeric:tabular-nums;
}
.credential__key{ font-size:.875rem; color:var(--slate); }

/* --- team ----------------------------------------------------------------- */
.team{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:0 2.5rem;
}
.team li{
  display:flex; align-items:center; gap:.85rem;
  padding:1.15rem 0; border-bottom:1px solid var(--rule);
}
.team__photo{
  width:46px; height:46px; flex:none;
  border-radius:50%; object-fit:cover;
  background:var(--paper-2);
  box-shadow:var(--shadow-sm), 0 0 0 1px var(--rule);
}
.team__text{ min-width:0; }
.team__name{
  font-family:var(--display); font-weight:700; font-size:.9375rem;
  color:var(--navy); letter-spacing:.01em;
}
.team__role{ font-size:.875rem; color:var(--slate); }

/* --- CTA band ------------------------------------------------------------- */
.band{ background:var(--navy); color:#fff; }
.band h2{ color:#fff; }
.band__grid{
  display:grid; grid-template-columns:1.2fr .8fr;
  gap:clamp(2rem,4vw,3.5rem); align-items:center;
}
.band__lede{ color:var(--pale); margin-top:1rem; font-size:1.0625rem; }
.band__actions{ display:flex; flex-wrap:wrap; gap:.85rem; justify-content:flex-end; }
.band__actions .btn{ --btn-bg:var(--cyan); --btn-fg:#fff; border-color:var(--cyan); }
.band__actions .btn--onDark{ --btn-bg:transparent; border-color:var(--rule-invert); }

/* --- footer --------------------------------------------------------------- */
.site-footer{ background:var(--navy-deep); color:var(--pale); padding-block:clamp(3.5rem,6vw,5rem) 2rem; }
.site-footer h3{
  color:#fff; font-size:.7rem; font-weight:700;
  letter-spacing:.18em; text-transform:uppercase; margin:0 0 1.1rem;
}
.footer__grid{
  display:grid; grid-template-columns:1.4fr 1fr 1fr 1.2fr;
  gap:clamp(2rem,4vw,3rem);
}
.site-footer svg{ height:26px; width:auto; margin-bottom:1.25rem; }
.footer__list{ list-style:none; margin:0; padding:0; display:grid; gap:.5rem; }
.footer__list a{ font-size:.9375rem; text-decoration:none; color:var(--pale); transition:color .18s var(--ease); }
.footer__list a:hover{ color:#fff; }
.footer__body{ font-size:.9375rem; line-height:1.7; }
.footer__bottom{
  margin-top:3rem; padding-top:1.5rem;
  border-top:1px solid var(--rule-invert);
  display:flex; flex-wrap:wrap; gap:1rem; justify-content:space-between;
  font-size:.875rem; color:var(--slate);
}

/* --- motion ----------------------------------------------------------------
   One vocabulary everywhere: things rise a little, resolve from soft to sharp,
   and decelerate into place. Nothing slides sideways, nothing bounces.
   --------------------------------------------------------------------------- */

/* Scoped to .js so a script failure can never leave the page blank. */
.js [data-reveal]{
  opacity:0;
  transform:translateY(28px) scale(.985);
  transition:opacity .7s var(--ease-out),
             transform .7s var(--ease-out);
  transition-delay:var(--seq,0s);
  will-change:transform, opacity;
}
[data-reveal].is-in{ opacity:1; transform:none; }
[data-reveal].is-in{ will-change:auto; }

/* --seq is the whole block's offset; --stagger is each child's place in the
   queue. Children add the two, so shifting a block moves its contents with it
   instead of leaving them behind. */
[data-reveal-delay="1"]{ --seq:.08s; }
[data-reveal-delay="2"]{ --seq:.16s; }
[data-reveal-delay="3"]{ --seq:.24s; }

/* Children arrive in sequence once their container is in view. */
[data-reveal].is-in .svc-item,
[data-reveal].is-in .credential,
[data-reveal].is-in .office,
[data-reveal].is-in .team li,
[data-reveal].is-in .point,
[data-reveal].is-in .shot,
[data-reveal].is-in .tally__item{
  animation:settle .8s var(--ease-out) both;
  animation-delay:calc(var(--seq,0s) + var(--stagger,0s));
}

[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(1){ --stagger:.03s; }
[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(2){ --stagger:.075s; }
[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(3){ --stagger:.12s; }
[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(4){ --stagger:.165s; }
[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(5){ --stagger:.21s; }
[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(6){ --stagger:.255s; }
[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(7){ --stagger:.3s; }
[data-reveal].is-in :is(.svc-item,.credential,.office,.team li,.point,.shot,.tally__item):nth-child(8){ --stagger:.345s; }

@keyframes settle{
  from{ opacity:0; transform:translateY(16px); }
  to{   opacity:1; transform:none; }
}

/* --- page-load sequence ----------------------------------------------------
   Blur-to-sharp only here: it is expensive to composite, so it runs once, on a
   handful of elements, and never on scroll.
   --------------------------------------------------------------------------- */
.hero__grid > * > *{ animation:focusIn var(--d-slow) var(--ease-out) both; }
.hero__grid > *:first-child > *:nth-child(1){ animation-delay:.05s; }
.hero__grid > *:first-child > *:nth-child(2){ animation-delay:.13s; }
.hero__grid > *:first-child > *:nth-child(3){ animation-delay:.21s; }
.hero__grid > *:first-child > *:nth-child(4){ animation-delay:.29s; }
.hero__grid > *:first-child > *:nth-child(5){ animation-delay:.37s; }
.hero__grid > .panel{ animation:panelIn 1.05s var(--ease-out) both; animation-delay:.18s; }
.hero__grid > .panel > *{ animation:none; }

@keyframes focusIn{
  from{ opacity:0; transform:translateY(20px); filter:blur(6px); }
  to{   opacity:1; transform:none;             filter:blur(0); }
}
@keyframes panelIn{
  from{ opacity:0; transform:translateY(32px) scale(.97); }
  to{   opacity:1; transform:none; }
}

/* Coverage rows stagger in each time the territory changes. */
.panel__row{ animation:settle .45s var(--ease-out) both; }
.panel__row:nth-child(1){ animation-delay:.02s; }
.panel__row:nth-child(2){ animation-delay:.06s; }
.panel__row:nth-child(3){ animation-delay:.10s; }
.panel__row:nth-child(4){ animation-delay:.14s; }
.panel__row:nth-child(5){ animation-delay:.18s; }
.panel__row:nth-child(6){ animation-delay:.22s; }

/* Ambient wash behind the hero — gives the panel something to sit in front of. */
.hero{ position:relative; isolation:isolate; }
.hero::before{
  content:"";
  position:absolute; z-index:-1;
  right:-25%; top:-45%;
  width:105%; aspect-ratio:1;
  background:radial-gradient(circle at 55% 45%,
              rgba(0,165,223,.085) 0%,
              rgba(0,165,223,.045) 28%,
              rgba(22,54,81,.02)  52%,
              transparent 74%);
  pointer-events:none;
}

/* --- responsive ----------------------------------------------------------- */
@media (max-width:960px){
  .hero__grid,.split,.band__grid{ grid-template-columns:1fr; }
  .band__actions{ justify-content:flex-start; }
  .footer__grid{ grid-template-columns:1fr 1fr; }
  .mega__grid{ grid-template-columns:1fr; gap:1.75rem; padding-block:1.75rem; }

  .nav{
    position:fixed; inset:76px 0 auto 0;
    flex-direction:column; align-items:stretch; gap:0;
    background:var(--paper); border-bottom:1px solid var(--rule);
    padding:1rem var(--gutter) 2rem;
    max-height:calc(100dvh - 76px); overflow-y:auto;
    display:none;
  }
  .nav[data-open="true"]{ display:flex; }
  .nav__link{ padding-block:.9rem; border-bottom:1px solid var(--rule); text-align:left; }
  .nav__link::after{ display:none; }
  .nav .btn{ margin-top:1rem; justify-content:center; }
  .mega{ position:static; box-shadow:none; border:0; opacity:1; visibility:visible; transform:none; display:none; }
  .mega[data-open="true"]{ display:block; }
  .nav__toggle{
    display:inline-flex; align-items:center; justify-content:center;
    margin-left:auto; width:44px; height:44px;
    background:none; border:1px solid var(--rule); border-radius:2px; cursor:pointer;
  }
  /* Comfortable tap targets on touch. */
  .panel__pill{ min-height:44px; }
  .svc-item a,.svc-item__static{ padding-block:1.35rem; }
}
@media (max-width:560px){
  .footer__grid{ grid-template-columns:1fr; }
  .team{ grid-template-columns:1fr; }
}

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  /* Delays matter as much as durations here: a staggered child with
     animation-fill-mode:both sits at opacity 0 for the whole delay, so
     collapsing only the duration leaves the reader staring at a gap before
     the content pops in. Both have to go. */
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-delay:0s !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    transition-delay:0s !important;
  }
  .js [data-reveal]{ opacity:1; transform:none; }
}

/* ==========================================================================
   Interior pages
   ========================================================================== */

/* --- breadcrumbs ---------------------------------------------------------- */
.crumbs{ border-bottom:1px solid var(--rule); background:var(--paper); }
.crumbs .wrap{
  display:flex; flex-wrap:wrap; align-items:center; gap:.55rem;
  padding-block:.9rem;
  font-size:.8125rem; color:var(--slate);
}
.crumbs a{ color:var(--slate); text-decoration:none; transition:color var(--d-fast) var(--ease-soft); }
.crumbs a:hover{ color:var(--cyan-deep); }
.crumbs [aria-current]{ color:var(--navy); }

/* --- interior page hero --------------------------------------------------- */
.page-hero{
  position:relative; isolation:isolate; overflow:hidden;
  padding-block:clamp(3rem,6vw,5rem) clamp(2.5rem,5vw,4rem);
}
.page-hero::before{
  content:""; position:absolute; z-index:-1;
  right:-20%; top:-60%; width:80%; aspect-ratio:1;
  background:radial-gradient(circle at 55% 45%,
              rgba(0,165,223,.07) 0%, rgba(0,165,223,.035) 30%, transparent 70%);
  pointer-events:none;
}
.page-hero h1{ max-width:20ch; }
.page-hero .lede{ margin-top:1.25rem; }
.page-hero > .wrap > *{ animation:focusIn var(--d-slow) var(--ease-out) both; }
.page-hero > .wrap > *:nth-child(1){ animation-delay:.04s; }
.page-hero > .wrap > *:nth-child(2){ animation-delay:.12s; }
.page-hero > .wrap > *:nth-child(3){ animation-delay:.20s; }
.page-hero > .wrap > *:nth-child(4){ animation-delay:.28s; }

/* --- numbered points -------------------------------------------------------
   Numbers are used here because the list is a real sequence of what an
   engagement covers, not decoration.
   --------------------------------------------------------------------------- */
.points{ list-style:none; margin:0; padding:0; display:grid; gap:0; counter-reset:pt; }
.point{
  display:grid; grid-template-columns:auto 1fr; gap:1.5rem;
  padding:1.75rem 0; border-top:1px solid var(--rule);
}
.point:last-child{ border-bottom:1px solid var(--rule); }
.point__num{
  font-family:var(--display); font-weight:800; font-size:.8125rem;
  color:var(--cyan); letter-spacing:.06em;
  font-variant-numeric:tabular-nums; padding-top:.25rem;
}
.point__name{ font-size:1.0625rem; margin:0 0 .35rem; }
.point__body{ margin:0; color:#54636F; max-width:62ch; }

/* --- team biographies ------------------------------------------------------ */
.bios{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
  gap:0 3rem;
}
.bio{ padding:1.9rem 0; border-top:1px solid var(--rule); }
/* Circular, and not only as a style choice: one of the supplied photographs is
   already a circle on a flat dark card, so a square frame would drag its
   corners in with it. A round mask makes seven very different sources - two
   phone snaps, four WordPress thumbnails and one AI-enhanced headshot - sit
   the same way. The hairline ring keeps the paler backgrounds from dissolving
   into the page. */
.bio__photo{
  width:76px; height:76px; margin:0 0 1rem;
  border-radius:50%; object-fit:cover;
  background:var(--paper-2);
  box-shadow:var(--shadow-sm), 0 0 0 1px var(--rule);
}
.bio__name{ font-size:1.0625rem; margin:0 0 .15rem; }
.bio__role{
  font-family:var(--display); font-size:.8125rem; font-weight:600;
  color:var(--cyan-deep); margin:0 0 .8rem;
}
.bio__body{ color:#54636F; font-size:.9375rem; margin:0 0 .8rem; }
.bio__tags{
  margin:0; font-size:.8125rem; color:var(--slate);
  padding-top:.7rem; border-top:1px dashed var(--rule);
}

/* --- form ------------------------------------------------------------------ */
.form{ display:grid; gap:1.1rem; }
.field{ display:grid; gap:.4rem; }
.field label{
  font-family:var(--display); font-size:.8125rem; font-weight:600; color:var(--navy);
}
.field input,.field select,.field textarea{
  font:inherit; font-size:.9375rem; color:var(--ink);
  padding:.8rem .9rem;
  background:var(--paper);
  border:1px solid var(--rule-strong);
  border-radius:var(--r-sm);
  transition:border-color var(--d-fast) var(--ease-soft),
             box-shadow var(--d-fast) var(--ease-soft);
}
.field textarea{ resize:vertical; min-height:110px; }
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none; border-color:var(--cyan);
  box-shadow:0 0 0 3px rgba(0,165,223,.16);
}
.form__note{ font-size:.8125rem; color:var(--slate); margin:.35rem 0 0; }

/* Floating label - only where a label and its input can share one line of
   vertical space (text/email/tel/textarea). Selects always show a value, so
   a floating label there would either sit floated permanently or overlap
   the chosen option; left as a plain label above instead. */
.field--float{ position:relative; }
.field--float input,.field--float textarea{ padding-top:1.2rem; padding-bottom:.4rem; }
.field--float label{
  position:absolute; left:.65rem; top:.8rem;
  padding:0 .25rem;
  font:inherit; font-size:.9375rem; color:var(--slate);
  background:var(--paper);
  transform-origin:left top;
  transition:transform .16s var(--ease-soft), color .16s var(--ease-soft);
  pointer-events:none; will-change:transform;
}
.field--float input:focus + label,
.field--float input:not(:placeholder-shown) + label,
.field--float textarea:focus + label,
.field--float textarea:not(:placeholder-shown) + label{
  transform:translateY(-1.3rem) scale(.78);
  color:var(--navy);
  font-family:var(--display); font-weight:600;
}
@media (prefers-reduced-motion:reduce){
  .field--float label{ transition:none; }
}

/* --- map -------------------------------------------------------------------- */
.map{
  border-radius:var(--r-md); overflow:hidden;
  border:1px solid var(--rule); box-shadow:var(--shadow-sm);
  line-height:0;
}

/* --- gallery ---------------------------------------------------------------- */
.gallery{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:1.25rem;
}
.shot{ margin:0; }
.shot__frame{
  aspect-ratio:4/3; display:grid; place-items:center;
  border-radius:var(--r-md);
  background:var(--paper);
  border:1px dashed var(--rule-strong);
  color:var(--pale);
}
.shot figcaption{ margin-top:.6rem; font-size:.8125rem; color:var(--slate); }

/* --- static list items (no link) -------------------------------------------- */
/* Static rows share the index grid; .svc-item__static is the non-link box. */
.svc-item__tag{
  display:inline-block; margin-bottom:.35rem;
  font-family:var(--display); font-size:.66rem; font-weight:700;
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--cyan-deep); background:rgba(0,165,223,.08);
  padding:.22rem .55rem; border-radius:999px;
}
.svc-item__name--plain{ display:block; }

/* An index set inside one column of a split has no room for the third
   column, so it keeps the stacked arrangement at every width. */
.svc-list--single .svc-item a{ grid-template-columns:2.5rem minmax(0,1fr); }
.svc-list--single .svc-item__desc{ grid-column:2/-1; }
.svc-list--single .svc-item__go{ display:none; }
.section__more{ margin-top:2.25rem; }

@media (max-width:560px){
  .bios{ grid-template-columns:1fr; }
  .point{ grid-template-columns:1fr; gap:.5rem; }
}

/* The form is a grid, so its button would otherwise stretch the full column. */
.form > .btn{ justify-self:start; }

/* ==========================================================================
   Motion layer 2
   Techniques taken from the tier-1 finance/SaaS front pages (Stripe, Linear,
   Ramp, Mercury, Vercel) and fitted only where this page already wanted
   them. House rules: transform and opacity only, nothing on the main thread
   that scroll can starve, and every effect degrades to the static page when
   the feature is missing or the visitor has asked for less motion.
   ========================================================================== */

/* --- 1 - cross-page view transitions --------------------------------------
   Stripe and Linear feel like applications largely because the page never
   blinks white between routes. One declaration buys that for a multi-page
   PHP site; browsers without it navigate exactly as they did before.
   --------------------------------------------------------------------------- */
@view-transition{ navigation:auto; }
::view-transition-old(root){ animation:vtOut .18s var(--ease-soft) both; }
::view-transition-new(root){ animation:vtIn  .28s var(--ease-out)  both; }
@keyframes vtOut{ to  { opacity:0; transform:translateY(-8px); } }
@keyframes vtIn { from{ opacity:0; transform:translateY(12px); } }

/* --- 2 - scroll progress rail ---------------------------------------------
   Scroll-driven CSS: no listener, no rAF, runs on the compositor. It reads
   as a progress bar, which suits a firm that sells deadlines.
   --------------------------------------------------------------------------- */
.progress{
  position:absolute; left:0; right:0; bottom:-1px; height:2px; z-index:2;
  transform:scaleX(0); transform-origin:0 50%;
  background:linear-gradient(90deg,var(--cyan) 0%,#29B2EC 60%,var(--pale) 100%);
  opacity:0; pointer-events:none;
  transition:opacity var(--d-mid) var(--ease-soft);
}
@supports (animation-timeline:scroll()){
  .site-header[data-stuck="true"] .progress{ opacity:1; }
  .progress{ animation:progressFill linear both; animation-timeline:scroll(root block); }
}
@keyframes progressFill{ from{ transform:scaleX(0); } to{ transform:scaleX(1); } }

/* --- 3 - cursor spotlight -------------------------------------------------
   Linear's signature effect, in two parts: a soft fill wash beneath the
   content, and a 1px gradient ring traced on the border. The ring is masked
   to the border box so it can never wash across text - the mistake that
   makes most copies of this effect unreadable. Position arrives as
   --mx/--my percentages from a single pointermove listener.
   --------------------------------------------------------------------------- */
.panel{ position:relative; isolation:isolate; overflow:hidden; }
.panel > *{ position:relative; z-index:1; }

.panel::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(420px circle at var(--mx,50%) var(--my,0%),
              rgba(0,165,223,.30) 0%, rgba(0,165,223,.10) 34%, transparent 68%);
  opacity:0; transition:opacity .4s var(--ease-soft);
}
.panel::after{
  content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
  border-radius:inherit; padding:1px;
  background:radial-gradient(280px circle at var(--mx,50%) var(--my,0%),
              rgba(143,220,251,.85) 0%, rgba(143,220,251,.18) 40%, transparent 65%);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:0; transition:opacity .4s var(--ease-soft);
}
.panel[data-lit="true"]::before,
.panel[data-lit="true"]::after{ opacity:1; }

/* The closing band is the other navy surface, so it takes the wash only - a
   border ring on a full-bleed section would have no corner to trace. */
.band{ position:relative; isolation:isolate; overflow:hidden; }
.band > *{ position:relative; z-index:1; }
.band::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(520px circle at var(--mx,50%) var(--my,50%),
              rgba(0,165,223,.22) 0%, rgba(0,165,223,.07) 38%, transparent 70%);
  opacity:0; transition:opacity .45s var(--ease-soft);
}
.band[data-lit="true"]::before{ opacity:1; }

/* --- 4 - word-mask headline reveal ----------------------------------------
   Ramp and Mercury set headlines rising out of their own baseline rather
   than fading in; it reads as typesetting arriving instead of a slide. The
   clip needs vertical breathing room or descenders get shaved, hence the
   padding and negative-margin pair.
   --------------------------------------------------------------------------- */
.w{
  display:inline-block; overflow:hidden; vertical-align:top;
  padding-block:.09em; margin-block:-.09em;
}
.w__i{
  display:inline-block;
  animation:wordUp .82s var(--ease-out) both;
  animation-delay:calc(var(--i,0) * .042s + .05s);
}
@keyframes wordUp{
  from{ transform:translateY(112%); opacity:0; }
  to  { transform:none;             opacity:1; }
}
/* A split headline supplies its own entrance, so drop it from the page-load
   sequence rather than running the two over each other. */
.hero__grid > * > h1.is-split,
.page-hero > .wrap > h1.is-split{ animation:none; }

/* --- 5 - counting figures -------------------------------------------------
   Wise, Ramp and Brex all animate their numbers, because on a finance site
   the number is the product. Tabular figures hold the width steady so the
   line cannot reflow while it counts.
   --------------------------------------------------------------------------- */
.tally{
  display:flex; flex-wrap:wrap; justify-content:space-between; gap:.6rem 2.25rem;
  margin:0 0 clamp(2.5rem,5vw,3.75rem); padding-top:1.35rem;
  border-top:1px solid var(--rule);
  list-style:none;
}
.tally__item{ display:flex; align-items:baseline; gap:.5rem; }
.tally__n{
  font-family:var(--display); font-weight:800; font-size:1.5rem;
  color:var(--navy); letter-spacing:-.02em;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
}
.tally__k{ font-size:.875rem; color:var(--slate); }
.page-hero .tally{ max-width:56ch; }

/* --- 6 - service ticker ---------------------------------------------------
   The seamless marquee every SaaS front page runs its logo wall on. Here it
   carries the fourteen service names, so it bridges the hero into the
   services section instead of decorating nothing. Duplicated track, -50%
   travel, edges masked rather than faded against a hard-coded colour.
   --------------------------------------------------------------------------- */
.ticker{
  overflow:hidden; border-block:1px solid var(--rule); background:var(--paper);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
}
.ticker__track{
  display:flex; width:max-content;
  animation:marquee 68s linear infinite;
}
.ticker:hover .ticker__track{ animation-play-state:paused; }
.ticker__item{
  display:flex; align-items:center; gap:1.4rem;
  padding:.95rem 0 .95rem 1.4rem; white-space:nowrap;
  font-family:var(--display); font-size:.8125rem; font-weight:600;
  letter-spacing:.02em; color:var(--slate);
}
.ticker__item::after{
  content:""; flex:none; width:3px; height:3px; border-radius:50%;
  background:var(--pale);
}
@keyframes marquee{
  from{ transform:translate3d(0,0,0); }
  to  { transform:translate3d(-50%,0,0); }
}

/* --- 7 - magnetic buttons -------------------------------------------------
   The pull is routed through custom properties so it composes with the
   existing hover lift and press instead of overwriting them.
   --------------------------------------------------------------------------- */
.btn{
  --pull-x:0px; --pull-y:0px; --lift:0px;
  transform:translate3d(var(--pull-x),calc(var(--pull-y) + var(--lift)),0);
  transition:background var(--d-fast) var(--ease-soft),
             border-color var(--d-fast) var(--ease-soft),
             box-shadow var(--d-mid) var(--ease-out),
             transform .28s var(--ease-out);
}
.btn:hover{
  --lift:-1px;
  transform:translate3d(var(--pull-x),calc(var(--pull-y) + var(--lift)),0);
}
.btn:active{
  --lift:0px;
  transform:translate3d(var(--pull-x),var(--pull-y),0) scale(.978);
  transition-duration:.09s;
}

/* --- 8 - sticky split column ----------------------------------------------
   Stripe and Apple pin the argument and scroll the evidence past it. These
   sections are already a claim beside a list, so the pattern costs no
   markup - applied only where the right column is the longer one.
   --------------------------------------------------------------------------- */
@media (min-width:961px){
  .split--sticky > *:first-child{
    position:sticky;
    top:calc(64px + clamp(1.75rem,4vw,3.5rem));
  }
}

/* --- 9 - hero parallax on exit --------------------------------------------
   Scroll-driven, so it costs nothing on the main thread. The hero recedes
   as it leaves rather than simply scrolling off.
   --------------------------------------------------------------------------- */
@supports (animation-timeline:view()){
  @media (min-width:961px) and (prefers-reduced-motion:no-preference){
    .hero__grid{
      animation:heroPark linear both;
      animation-timeline:view();
      animation-range:exit 15% exit 95%;
    }
  }
}
@keyframes heroPark{
  from{ opacity:1;   transform:none; }
  to  { opacity:.42; transform:translateY(-26px) scale(.985); }
}

/* --- 10 - ambient drift ---------------------------------------------------
   The wash behind the hero was static. Two layers drifting against each
   other on different periods never visibly repeat, and transform-only means
   the browser composites it for free.
   --------------------------------------------------------------------------- */
.hero::before{ animation:drift 38s var(--ease-soft) infinite alternate; }
.hero::after{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:-28%; bottom:-45%; width:72%; aspect-ratio:1;
  background:radial-gradient(circle at 50% 50%,
              rgba(22,54,81,.06) 0%, rgba(0,165,223,.032) 42%, transparent 72%);
  animation:driftB 52s var(--ease-soft) infinite alternate;
}
@keyframes drift { to{ transform:translate3d(-4%,3.5%,0) scale(1.09); } }
@keyframes driftB{ to{ transform:translate3d(5%,-4%,0)  scale(1.12); } }

/* Eyebrow rules draw themselves in alongside the block they label. */
[data-reveal].is-in .eyebrow::after{
  transform-origin:left;
  animation:ruleDraw .85s var(--ease-out) both .12s;
}
@keyframes ruleDraw{ from{ transform:scaleX(0); } to{ transform:scaleX(1); } }

/* --- reduced motion --------------------------------------------------------
   The global rule collapses durations, which is the wrong answer for
   anything that loops or is driven by scroll - those have to be switched
   off outright.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .ticker__track,
  .hero::before,
  .hero::after,
  .page-hero::before,
  .hero__grid,
  .page-hero > .wrap,
  .progress{ animation:none !important; }
  .ticker__track{ transform:none; }
  .progress{ opacity:0 !important; }
  .w__i{ animation:none !important; transform:none; opacity:1; }
  .panel::before,.panel::after,.band::before{ transition:none; }
}

@media (max-width:960px){
  /* Touch has no cursor to track, and the sticky offset assumes a desk. */
  .panel::before,.panel::after,.band::before{ display:none; }
  .tally{ gap:.5rem 1.5rem; }
  .tally__n{ font-size:1.3rem; }
}

/* ==========================================================================
   Motion layer 2a - coverage and hygiene
   Closes the gaps found auditing the layer above page by page: interior
   pages were static where the homepage breathed, three lists arrived all at
   once, and the looping animations never stopped.
   ========================================================================== */

/* --- interior pages get the same ambient wash ------------------------------
   The homepage drifted and every other page sat still, which read as the
   homepage being the only finished one. Slower here: these heroes are short,
   so the same period would look faster than it does on the home page.
   --------------------------------------------------------------------------- */
.page-hero::before{ animation:drift 46s var(--ease-soft) infinite alternate; }

/* ...and the same scroll-linked exit. */
@supports (animation-timeline:view()){
  @media (min-width:961px) and (prefers-reduced-motion:no-preference){
    .page-hero > .wrap{
      animation:heroPark linear both;
      animation-timeline:view();
      animation-range:exit 15% exit 95%;
    }
  }
}

/* --- mega menu items arrive in sequence ------------------------------------
   Fourteen links appearing simultaneously is a wall. Staggering them gives
   the eye a column order to follow, which is the whole point of the panel.
   --------------------------------------------------------------------------- */
.mega[data-open="true"] .mega__list li{ animation:settle .5s var(--ease-out) both; }
.mega[data-open="true"] .mega__list li:nth-child(1){ animation-delay:.02s; }
.mega[data-open="true"] .mega__list li:nth-child(2){ animation-delay:.05s; }
.mega[data-open="true"] .mega__list li:nth-child(3){ animation-delay:.08s; }
.mega[data-open="true"] .mega__list li:nth-child(4){ animation-delay:.11s; }
.mega[data-open="true"] .mega__list li:nth-child(5){ animation-delay:.14s; }
.mega[data-open="true"] .mega__list li:nth-child(6){ animation-delay:.17s; }

/* --- will-change hygiene ---------------------------------------------------
   A permanent will-change holds a compositor layer for the life of the page,
   which is the opposite of what the property is for. Promote a button only
   while it is actually being pushed around.
   --------------------------------------------------------------------------- */
.btn{ will-change:auto; }
.btn:hover,
.btn:focus-visible{ will-change:transform; }

/* --- stop the loops when nobody is looking ---------------------------------
   The marquee and the two hero washes are infinite, so without this they
   keep the compositor busy for as long as the tab is open - on a laptop
   that is measurable battery for pixels nobody is looking at. The flag is
   set by one IntersectionObserver.
   --------------------------------------------------------------------------- */
.hero[data-inview="false"]::before,
.hero[data-inview="false"]::after,
.page-hero[data-inview="false"]::before,
.ticker[data-inview="false"] .ticker__track{ animation-play-state:paused; }

/* ==========================================================================
   Call-back form - validation and delivery states
   The form delivers: api/contact.php mails the office and keeps a copy on
   disk. Three states are styled here, and the rule behind all of them is that
   the page never claims more than it knows - what is wrong, that it arrived,
   or that it did not.
   ========================================================================== */

:root{
  /* Desaturated enough to sit beside the navy without shouting. */
  --flag:      #C0453B;
  --flag-soft: rgba(192,69,59,.06);
}

/* --- honeypot ---------------------------------------------------------------
   Off-screen rather than display:none - bots increasingly skip fields that
   are hidden the obvious way. Never focusable, never announced.
   --------------------------------------------------------------------------- */
.field--trap{
  position:absolute !important;
  left:-9999px; top:auto;
  width:1px; height:1px; overflow:hidden;
}

/* --- field errors ------------------------------------------------------------ */
.field__err{
  margin:.2rem 0 0;
  font-size:.8125rem; color:var(--flag);
  animation:errIn .32s var(--ease-out) both;
}
.field__err[hidden]{ display:none; }

.field :is(input,select,textarea)[aria-invalid="true"]{
  border-color:var(--flag);
  background:var(--flag-soft);
}
.field :is(input,select,textarea)[aria-invalid="true"]:focus{
  border-color:var(--flag);
  box-shadow:0 0 0 3px rgba(192,69,59,.16);
}
@keyframes errIn{
  from{ opacity:0; transform:translateY(-4px); }
  to  { opacity:1; transform:none; }
}

/* --- status notices ---------------------------------------------------------- */
.notice{
  margin:0;
  padding:.9rem 1.05rem;
  border:1px solid; border-radius:var(--r-sm);
  font-size:.9375rem; line-height:1.55;
  animation:noticeIn .4s var(--ease-out) both;
}
.notice[hidden]{ display:none; }
.notice strong{ display:block; font-family:var(--display); font-size:.875rem; margin-bottom:.2rem; }
.notice a{ color:var(--cyan-deep); text-underline-offset:3px; }

.notice[data-state="warn"]{
  border-color:rgba(0,165,223,.34);
  background:rgba(0,165,223,.055);
  color:var(--navy);
}
.notice[data-state="error"]{
  border-color:rgba(192,69,59,.38);
  background:var(--flag-soft);
  color:var(--navy);
}
@keyframes noticeIn{
  from{ opacity:0; transform:translateY(-6px); }
  to  { opacity:1; transform:none; }
}

@media (prefers-reduced-motion:reduce){
  .field__err,.notice{ animation:none !important; }
}

/* ==========================================================================
   Motion layer 3 - the ledger draws itself
   Layer 2 borrowed from Stripe, Linear and Ramp. This layer stops borrowing.
   The whole design language here is hairline rules and flush type - so the
   rules themselves are what should move. On an accounting site a ledger that
   rules itself in is the one effect that could not have come off any other
   firm's front page.

   Same house rules as before: transform and opacity only, nothing that lays
   out, and every effect is guarded so the static page is always the fallback.
   ========================================================================== */

/* --- 1 - the rules draw in -------------------------------------------------
   The border stays exactly where it was and keeps its colour; it is only
   handed over to a pseudo-element that can be transformed, and only when
   there is script, a reveal container to time against, and no request for
   reduced motion. Any one of those missing and the plain border is what
   renders - which is why the border-colour hand-off lives inside the same
   guard as the animation and never outside it.

   The delay reuses --seq and --stagger, so a rule draws with the row it
   belongs to instead of running on a timeline of its own.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){

  /* rows ruled underneath */
  .js [data-reveal] :is(.svc-item,.team li){
    position:relative;
    border-bottom-color:transparent;
  }
  .js [data-reveal] :is(.svc-item,.team li)::after{
    content:""; position:absolute; left:0; right:0; bottom:-1px; height:1px;
    background:var(--rule);
    transform:scaleX(0); transform-origin:left;
  }

  /* rows ruled above */
  .js [data-reveal] :is(.credential,.office){
    position:relative;
    border-top-color:transparent;
  }
  .js [data-reveal] :is(.credential,.office)::after{
    content:""; position:absolute; left:0; right:0; top:-1px; height:1px;
    background:var(--rule);
    transform:scaleX(0); transform-origin:left;
  }

  .js [data-reveal].is-in :is(.svc-item,.team li,.credential,.office)::after{
    animation:ruleDraw .9s var(--ease-out) both;
    animation-delay:calc(var(--seq,0s) + var(--stagger,0s) + .06s);
  }

  /* A bio card carries its own [data-reveal] rather than sitting inside one,
     so it times against itself. Same rule, different hook. */
  .js .bio[data-reveal]{
    position:relative;
    border-top-color:transparent;
  }
  .js .bio[data-reveal]::after{
    content:""; position:absolute; left:0; right:0; top:-1px; height:1px;
    background:var(--rule);
    transform:scaleX(0); transform-origin:left;
  }
  .js .bio[data-reveal].is-in::after{
    animation:ruleDraw .9s var(--ease-out) both;
    animation-delay:calc(var(--seq,0s) + .06s);
  }

  /* The 2px navy rule under each service group is the heaviest line on the
     page, so it draws slower and leads its own list in. */
  .js [data-reveal] .svc-group__head{
    position:relative;
    border-bottom-color:transparent;
  }
  .js [data-reveal] .svc-group__head::after{
    content:""; position:absolute; left:0; right:0; bottom:-2px; height:2px;
    background:var(--navy);
    transform:scaleX(0); transform-origin:left;
  }
  .js [data-reveal].is-in .svc-group__head::after{
    animation:ruleDraw 1.05s var(--ease-out) both .04s;
  }
}

/* --- 2 - service row -------------------------------------------------------
   The cyan edge bar that used to live here has been removed. The row now
   wipes to a full navy field - see "The mark" in the service index above -
   and a 2px bar underneath a full fill is a bar nobody can see.
   Static list items are not links and must not look like they respond: the
   fill and the arrow are simply not rendered for them.
   --------------------------------------------------------------------------- */

/* --- 3 - footer links draw their underline ---------------------------------
   The footer had colour-only hovers, which on a navy field is nearly
   invisible. An underline that draws from the left is the smallest possible
   way to say the thing is a link, and it matches the rule vocabulary above.
   --------------------------------------------------------------------------- */
.footer__list a{ position:relative; display:inline-block; }
.footer__list a::after{
  content:""; position:absolute; left:0; right:0; bottom:-2px; height:1px;
  background:currentColor;
  transform:scaleX(0); transform-origin:left;
  transition:transform .28s var(--ease-out);
}
.footer__list a:hover::after,
.footer__list a:focus-visible::after{ transform:scaleX(1); }

@media (prefers-reduced-motion:reduce){
  .svc-item__fill,.svc-item__go,
  .svc-item a,.svc-item__name,.svc-item__desc,
  .footer__list a::after{ transition:none; }
}

/* ==========================================================================
   Editor content
   Only reached by the WordPress fallback template, for pages the client adds
   later. Its job is to keep new content inside the same rhythm as the built
   pages instead of arriving with block-editor defaults.
   ========================================================================== */
.prose{ max-width:68ch; }
.prose > * + *{ margin-top:1.1em; }
.prose h2{ margin-top:2.25em; }
.prose h3{ margin-top:1.75em; }
.prose ul,.prose ol{ padding-left:1.25rem; }
.prose li + li{ margin-top:.35rem; }
.prose a{ color:var(--cyan-deep); text-underline-offset:3px; }
.prose img{ border-radius:var(--r-md); }
.prose blockquote{
  margin:2rem 0; padding:.35rem 0 .35rem 1.35rem;
  border-left:2px solid var(--cyan);
  color:var(--navy); font-size:1.0625rem;
}
.prose table{ width:100%; border-collapse:collapse; font-size:.9375rem; }
.prose :is(th,td){ padding:.7rem .8rem; border-bottom:1px solid var(--rule); text-align:left; }
.prose th{ font-family:var(--display); font-weight:700; color:var(--navy); }
/* Wide content scrolls inside itself rather than pushing the page sideways. */
.prose .wp-block-table{ overflow-x:auto; }

/* --- sequenced split ------------------------------------------------------
   The claim arrives, settles and pins; the evidence follows beside it. Reading
   order and motion order agree, which is the whole reason the pattern works.

   The offset is 280ms on purpose. Below about 200ms the two columns read as
   simultaneous and the sequence is wasted; much above 300ms and the right half
   of a desktop viewport sits visibly empty, which reads as broken rather than
   deliberate. Children inherit --seq and add their own --stagger, so the
   evidence queue starts after the claim rather than racing it.

   Desktop only: stacked columns are already sequenced by being one above the
   other, and repeating it there would just delay content the reader has
   scrolled to.
   --------------------------------------------------------------------------- */
@media (min-width:961px){
  .split--sticky > [data-reveal]:first-child { --seq:0s; }
  .split--sticky > [data-reveal]:nth-child(2){ --seq:.28s; }
}

/* ==========================================================================
   Insights articles
   ========================================================================== */

/* Article titles are sentences, not slogans - at the interior h1 size a
   twelve-word title stacks five lines deep. */
.page-hero--article h1{ font-size:clamp(2rem,4.2vw,3.25rem); max-width:26ch; line-height:1.1; }
.page-hero--article .lede{ max-width:64ch; }
.article-meta{
  display:flex; flex-wrap:wrap; gap:.35rem .6rem; margin-top:1.5rem;
  font-size:.875rem; color:var(--slate);
}

.section--article{ padding-block:clamp(2.5rem,5vw,4rem) var(--section); border-top:1px solid var(--rule); }
.article{ display:grid; gap:2.5rem; }
@media (min-width:1000px){
  .article{ grid-template-columns:15rem minmax(0,1fr); gap:clamp(3rem,6vw,5.5rem); align-items:start; }
  .toc{ position:sticky; top:6.5rem; }
}

.toc{ font-size:.875rem; }
.toc__label{
  font-family:var(--display); font-size:.7rem; font-weight:700;
  letter-spacing:.2em; text-transform:uppercase; color:var(--cyan-deep);
  margin:0 0 .9rem;
}
.toc__list{ list-style:none; margin:0; padding:0; border-left:2px solid var(--rule); }
.toc__list a{
  display:block; padding:.4rem 0 .4rem 1rem; margin-left:-2px;
  border-left:2px solid transparent;
  color:#54636F; text-decoration:none; line-height:1.4;
  transition:color var(--d-fast) var(--ease-soft), border-color var(--d-fast) var(--ease-soft);
}
.toc__list a:hover{ color:var(--navy); border-left-color:var(--cyan); }
@media (max-width:999px){
  .toc{ padding:1.25rem 1.4rem; background:var(--paper-2); border-radius:var(--r-md); }
}

.prose{ max-width:70ch; font-size:1.125rem; line-height:1.75; color:#26333E; }
.prose > :first-child{ margin-top:0; }
.prose h2{
  font-size:clamp(1.5rem,2.4vw,1.9rem); line-height:1.2;
  margin:3.25rem 0 1.1rem; scroll-margin-top:6.5rem;
}
.prose h3{ font-size:1.1875rem; margin:2.1rem 0 .45rem; }
.prose h3 + p{ margin-top:0; }
.prose p{ margin:0 0 1.25em; }
.prose ul{ margin:0 0 1.4em; padding-left:1.3rem; }
.prose li{ margin-bottom:.55em; padding-left:.25rem; }
.prose li::marker{ color:var(--cyan); }
.prose strong{ color:var(--navy); }
/* The generic .prose link colour above would otherwise repaint the CTA buttons. */
.prose .btn{ color:var(--btn-fg); text-decoration:none; }

.prose .article-tags{
  list-style:none; margin:3rem 0 0; padding:1.5rem 0 0; border-top:1px solid var(--rule);
  display:flex; flex-wrap:wrap; gap:.5rem;
}
.prose .article-tags li{ margin:0;
  font-family:var(--display); font-size:.72rem; font-weight:600; letter-spacing:.04em;
  color:var(--navy-soft); background:rgba(0,165,223,.08);
  padding:.35rem .75rem; border-radius:999px;
}

.article-cta{
  margin-top:2.5rem; padding:clamp(1.75rem,4vw,2.5rem);
  background:var(--paper-2); border:1px solid var(--rule); border-radius:var(--r-lg);
}
.article-cta h2{ margin:0 0 .75rem; font-size:clamp(1.35rem,2.2vw,1.65rem); }
.article-cta p{ font-size:1.0625rem; }
.prose .article-cta__actions{ display:flex; flex-wrap:wrap; gap:.75rem; margin-top:1.5rem; }

/* Article index rows carry a tag above the name, so the name column is a
   wrapper; the tag has to follow the row into the navy fill on hover. */
.svc-item__head{ display:block; }
.svc-item__head .svc-item__name{ display:block; }
.svc-item:hover > a .svc-item__tag,
.svc-item:focus-within > a .svc-item__tag{ color:#fff; background:rgba(255,255,255,.14); }
.svc-list--articles .svc-item a{ padding-right:2.75rem; }
